diff --git a/dev/App/User.js b/dev/App/User.js index 829aee2b5..b4a2792f2 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -26,6 +26,7 @@ SettingsStore = require('Stores/User/Settings'), AccountStore = require('Stores/User/Account'), IdentityStore = require('Stores/User/Identity'), + TemplateStore = require('Stores/User/Template'), FolderStore = require('Stores/User/Folder'), PgpStore = require('Stores/User/Pgp'), @@ -587,6 +588,8 @@ oIdentity.name(Utils.pString(oIdentityData['Name'])); oIdentity.replyTo(Utils.pString(oIdentityData['ReplyTo'])); oIdentity.bcc(Utils.pString(oIdentityData['Bcc'])); + oIdentity.signature(Utils.pString(oIdentityData['Signature'])); + oIdentity.signatureInsertBefore(!!oIdentityData['SignatureInsertBefore']); return oIdentity; })); @@ -595,6 +598,29 @@ }); }; + AppUser.prototype.templates = function () + { + TemplateStore.templates.loading(true); + + Remote.templates(function (sResult, oData) { + + TemplateStore.templates.loading(false); + + if (Enums.StorageResultType.Success === sResult && oData.Result && + Utils.isArray(oData.Result['Templates'])) + { + Utils.delegateRunOnDestroy(TemplateStore.templates()); + + IdentityStore.templates(_.map(oData.Result['Templates'], function (oIdentityData) { + return { + 'id': Utils.pString(oIdentityData['Id']), + 'name': Utils.pString(oIdentityData['Name']) + }; + })); + } + }); + }; + AppUser.prototype.quota = function () { Remote.quota(function (sResult, oData) { diff --git a/dev/Common/Globals.js b/dev/Common/Globals.js index 32794cb2b..752d02797 100644 --- a/dev/Common/Globals.js +++ b/dev/Common/Globals.js @@ -135,7 +135,7 @@ 'removeButtons': 'Format,Undo,Redo,Cut,Copy,Paste,Anchor,Strike,Subscript,Superscript,Image,SelectAll,Source', 'removeDialogTabs': 'link:advanced;link:target;image:advanced;images:advanced', - 'extraPlugins': 'plain', // signature + 'extraPlugins': 'plain,signature', 'allowedContent': true, 'font_defaultLabel': 'Arial', diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js index e0b4c8593..ead03e929 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/HtmlEditor.js @@ -64,12 +64,16 @@ /** * @param {string} sSignature + * @param {bool} bHtml + * @param {bool} bInsertBefore */ - HtmlEditor.prototype.setSignature = function (sSignature) + HtmlEditor.prototype.setSignature = function (sSignature, bHtml, bInsertBefore) { if (this.editor) { this.editor.execCommand('insertSignature', { + 'isHtml': bHtml, + 'insertBefore': bInsertBefore, 'signature': sSignature }); } @@ -92,24 +96,46 @@ }; /** - * @param {boolean=} bWrapIsHtml = false + * @param {string} sText * @return {string} */ - HtmlEditor.prototype.getData = function (bWrapIsHtml) + HtmlEditor.prototype.clearSignatureSigns = function (sText) { + return sText + .replace("\u0002", '').replace("\u0002", '') + .replace("\u0002", '').replace("\u0002", '') + .replace("\u0003", '').replace("\u0003", '') + .replace("\u0003", '').replace("\u0003", '') + ; + } + /** + * @param {boolean=} bWrapIsHtml = false + * @param {boolean=} bClearSignatureSigns = false + * @return {string} + */ + HtmlEditor.prototype.getData = function (bWrapIsHtml, bClearSignatureSigns) + { + var sResult = ''; if (this.editor) { if ('plain' === this.editor.mode && this.editor.plugins.plain && this.editor.__plain) { - return this.editor.__plain.getRawData(); + sResult = this.editor.__plain.getRawData(); + } + else + { + sResult = bWrapIsHtml ? + '
' + + this.editor.getData() + '
' : this.editor.getData(); } - return bWrapIsHtml ? - '
' + - this.editor.getData() + '
' : this.editor.getData(); + if (bClearSignatureSigns) + { + sResult = this.clearSignatureSigns(sResult); + } } - return ''; + return sResult; }; HtmlEditor.prototype.modeToggle = function (bPlain) diff --git a/dev/External/ko.js b/dev/External/ko.js index ae142fbf2..84a9783f8 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -954,6 +954,25 @@ return this; }; + ko.observable.fn.deleteAccessHelper = function () + { + this.extend({'falseTimeout': 3000}).extend({'toggleSubscribe': [null, + function (oPrev) { + if (oPrev && oPrev.deleteAccess) + { + oPrev.deleteAccess(false); + } + }, function (oNext) { + if (oNext && oNext.deleteAccess) + { + oNext.deleteAccess(true); + } + } + ]}); + + return this; + }; + ko.observable.fn.validateFunc = function (fFunc) { var Utils = require('Common/Utils'); diff --git a/dev/Model/Contact.js b/dev/Model/Contact.js index 1467c72b6..5c653361d 100644 --- a/dev/Model/Contact.js +++ b/dev/Model/Contact.js @@ -112,7 +112,7 @@ /** * @return string */ - ContactModel.prototype.lineAsCcc = function () + ContactModel.prototype.lineAsCss = function () { var aResult = []; if (this.deleted()) diff --git a/dev/Model/Identity.js b/dev/Model/Identity.js index 7a6cef544..d28c201a3 100644 --- a/dev/Model/Identity.js +++ b/dev/Model/Identity.js @@ -7,8 +7,6 @@ _ = require('_'), ko = require('ko'), - Utils = require('Common/Utils'), - AbstractModel = require('Knoin/AbstractModel') ; @@ -28,6 +26,9 @@ this.replyTo = ko.observable(''); this.bcc = ko.observable(''); + this.signature = ko.observable(''); + this.signatureInsertBefore = ko.observable(false); + this.deleteAccess = ko.observable(false); this.canBeDeleted = ko.computed(function () { return '' !== this.id(); diff --git a/dev/Model/Message.js b/dev/Model/Message.js index c0fc83136..30a01c860 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -63,6 +63,7 @@ this.newForAnimation = ko.observable(false); this.deleted = ko.observable(false); + this.deletedMark = ko.observable(false); this.unseen = ko.observable(false); this.flagged = ko.observable(false); this.answered = ko.observable(false); @@ -322,6 +323,7 @@ this.newForAnimation(false); this.deleted(false); + this.deletedMark(false); this.unseen(false); this.flagged(false); this.answered(false); @@ -546,6 +548,7 @@ this.answered(!!oJsonMessage.IsAnswered); this.forwarded(!!oJsonMessage.IsForwarded); this.isReadReceipt(!!oJsonMessage.IsReadReceipt); + this.deletedMark(!!oJsonMessage.IsDeleted); bResult = true; } @@ -621,13 +624,17 @@ /** * @return string */ - MessageModel.prototype.lineAsCcc = function () + MessageModel.prototype.lineAsCss = function () { var aResult = []; if (this.deleted()) { aResult.push('deleted'); } + if (this.deletedMark()) + { + aResult.push('deleted-mark'); + } if (this.selected()) { aResult.push('selected'); @@ -972,6 +979,7 @@ this.answered(oMessage.answered()); this.forwarded(oMessage.forwarded()); this.isReadReceipt(oMessage.isReadReceipt()); + this.deletedMark(oMessage.deletedMark()); this.priority(oMessage.priority()); @@ -1339,8 +1347,8 @@ */ MessageModel.prototype.flagHash = function () { - return [this.deleted(), this.unseen(), this.flagged(), this.answered(), this.forwarded(), - this.isReadReceipt()].join(''); + return [this.deleted(), this.deletedMark(), this.unseen(), this.flagged(), this.answered(), this.forwarded(), + this.isReadReceipt()].join(','); }; module.exports = MessageModel; diff --git a/dev/Model/Template.js b/dev/Model/Template.js new file mode 100644 index 000000000..e8b53ae1d --- /dev/null +++ b/dev/Model/Template.js @@ -0,0 +1,50 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + ko = require('ko'), + + AbstractModel = require('Knoin/AbstractModel') + ; + + /** + * @constructor + * + * @param {string} sID + * @param {string} sName + * @param {string} sBody + */ + function TemplateModel(sID, sName, sBody) + { + AbstractModel.call(this, 'TemplateModel'); + + this.id = sID; + this.name = sName; + this.body = sBody; + + this.deleteAccess = ko.observable(false); + } + + _.extend(TemplateModel.prototype, AbstractModel.prototype); + + /** + * @type {string} + */ + TemplateModel.prototype.id = ''; + + /** + * @type {string} + */ + TemplateModel.prototype.name = ''; + + /** + * @type {string} + */ + TemplateModel.prototype.body = ''; + + module.exports = TemplateModel; + +}()); \ No newline at end of file diff --git a/dev/Screen/User/Settings.js b/dev/Screen/User/Settings.js index e477fa84a..201d394c6 100644 --- a/dev/Screen/User/Settings.js +++ b/dev/Screen/User/Settings.js @@ -85,6 +85,9 @@ 'SettingsChangePassword', 'SETTINGS_LABELS/LABEL_CHANGE_PASSWORD_NAME', 'change-password'); } +// kn.addSettingsViewModel(require('Settings/User/Templates'), +// 'SettingsTemplates', 'SETTINGS_LABELS/LABEL_TEMPLATES_NAME', 'templates'); + kn.addSettingsViewModel(require('Settings/User/Folders'), 'SettingsFolders', 'SETTINGS_LABELS/LABEL_FOLDERS_NAME', 'folders'); diff --git a/dev/Settings/User/Accounts.js b/dev/Settings/User/Accounts.js index d3a85c51a..43ffb65f6 100644 --- a/dev/Settings/User/Accounts.js +++ b/dev/Settings/User/Accounts.js @@ -9,6 +9,7 @@ ko = require('ko'), Enums = require('Common/Enums'), + Utils = require('Common/Utils'), Translator = require('Common/Translator'), Links = require('Common/Links'), @@ -34,33 +35,8 @@ return '' === this.processText() ? 'hidden' : 'visible'; }, this); - this.accountForDeletion = ko.observable(null).extend({'falseTimeout': 3000}).extend({'toggleSubscribe': [this, - function (oPrev) { - if (oPrev) - { - oPrev.deleteAccess(false); - } - }, function (oNext) { - if (oNext) - { - oNext.deleteAccess(true); - } - } - ]}); - - this.identityForDeletion = ko.observable(null).extend({'falseTimeout': 3000}).extend({'toggleSubscribe': [this, - function (oPrev) { - if (oPrev) - { - oPrev.deleteAccess(false); - } - }, function (oNext) { - if (oNext) - { - oNext.deleteAccess(true); - } - } - ]}); + this.accountForDeletion = ko.observable(null).deleteAccessHelper(); + this.identityForDeletion = ko.observable(null).deleteAccessHelper(); } AccountsUserSettings.prototype.scrollableOptions = function () @@ -158,6 +134,12 @@ } }; + AccountsUserSettings.prototype.accountsAndIdentitiesAfterMove = function () + { + Remote.accountsAndIdentitiesSortOrder(null, + AccountStore.accountsEmails.peek(), IdentityStore.identitiesIDS.peek()); + }; + AccountsUserSettings.prototype.onBuild = function (oDom) { var self = this; diff --git a/dev/Settings/User/Templates.js b/dev/Settings/User/Templates.js new file mode 100644 index 000000000..c0b1e8e9a --- /dev/null +++ b/dev/Settings/User/Templates.js @@ -0,0 +1,105 @@ + +(function () { + + 'use strict'; + + var + ko = require('ko'), + + Translator = require('Common/Translator'), + + TemplateStore = require('Stores/User/Template'), + + Remote = require('Storage/User/Remote') + ; + + /** + * @constructor + */ + function TemplatesUserSettings() + { + this.templates = TemplateStore.templates; + + this.processText = ko.computed(function () { + return TemplateStore.templates.loading() ? Translator.i18n('SETTINGS_TEMPLETS/LOADING_PROCESS') : ''; + }, this); + + this.visibility = ko.computed(function () { + return '' === this.processText() ? 'hidden' : 'visible'; + }, this); + + this.templateForDeletion = ko.observable(null).deleteAccessHelper(); + } + + TemplatesUserSettings.prototype.scrollableOptions = function () + { + return { + handle: '.drag-handle' + }; + }; + + TemplatesUserSettings.prototype.addNewTemplate = function () + { + require('Knoin/Knoin').showScreenPopup(require('View/Popup/Template')); + }; + + TemplatesUserSettings.prototype.editTemplate = function (oTemplateItem) + { + if (oTemplateItem) + { + require('Knoin/Knoin').showScreenPopup(require('View/Popup/Template'), [oTemplateItem]); + } + }; + + /** + * @param {AccountModel} oTemplateToRemove + */ + TemplatesUserSettings.prototype.deleteTemplate = function (oTemplateToRemove) + { + if (oTemplateToRemove && oTemplateToRemove.deleteAccess()) + { + this.templateForDeletion(null); + + var + self = this, + fRemoveAccount = function (oAccount) { + return oTemplateToRemove === oAccount; + } + ; + + if (oTemplateToRemove) + { + this.templates.remove(fRemoveAccount); + + Remote.templateDelete(function () { + self.reloadTemplates(); + }, oTemplateToRemove.id); + } + } + }; + + TemplatesUserSettings.prototype.reloadTemplates = function () + { + require('App/User').templates(); + }; + + TemplatesUserSettings.prototype.onBuild = function (oDom) + { + var self = this; + + oDom + .on('click', '.templates-list .template-item .e-action', function () { + var oTemplateItem = ko.dataFor(this); + if (oTemplateItem) + { + self.editTemplate(oTemplateItem); + } + }) + ; + + this.reloadTemplates(); + }; + + module.exports = TemplatesUserSettings; + +}()); \ No newline at end of file diff --git a/dev/Storage/User/Cache.js b/dev/Storage/User/Cache.js index 6d483fb98..e83773b65 100644 --- a/dev/Storage/User/Cache.js +++ b/dev/Storage/User/Cache.js @@ -313,6 +313,7 @@ oMessage.answered(!!aFlags[2]); oMessage.forwarded(!!aFlags[3]); oMessage.isReadReceipt(!!aFlags[4]); + oMessage.deletedMark(!!aFlags[5]); } if (0 < oMessage.threads().length) @@ -343,7 +344,8 @@ this.setMessageFlagsToCache( oMessage.folderFullNameRaw, oMessage.uid, - [oMessage.unseen(), oMessage.flagged(), oMessage.answered(), oMessage.forwarded(), oMessage.isReadReceipt()] + [oMessage.unseen(), oMessage.flagged(), oMessage.answered(), oMessage.forwarded(), + oMessage.isReadReceipt(), oMessage.deletedMark()] ); } }; diff --git a/dev/Storage/User/Remote.js b/dev/Storage/User/Remote.js index 7a467e8e6..a9b6178e5 100644 --- a/dev/Storage/User/Remote.js +++ b/dev/Storage/User/Remote.js @@ -188,11 +188,13 @@ /** * @param {?Function} fCallback * @param {Array} aAccounts + * @param {Array} aIdentities */ - RemoteUserStorage.prototype.accountSortOrder = function (fCallback, aAccounts) + RemoteUserStorage.prototype.accountsAndIdentitiesSortOrder = function (fCallback, aAccounts, aIdentities) { - this.defaultRequest(fCallback, 'AccountSortOrder', { - 'Accounts': aAccounts + this.defaultRequest(fCallback, 'AccountsAndIdentitiesSortOrder', { + 'Accounts': aAccounts, + 'Identities': aIdentities }); }; @@ -203,15 +205,20 @@ * @param {string} sName * @param {string} sReplyTo * @param {string} sBcc + * @param {string} sSignature + * @param {boolean} bSignatureInsertBefore */ - RemoteUserStorage.prototype.identityUpdate = function (fCallback, sId, sEmail, sName, sReplyTo, sBcc) + RemoteUserStorage.prototype.identityUpdate = function (fCallback, sId, sEmail, sName, sReplyTo, sBcc, + sSignature, bSignatureInsertBefore) { this.defaultRequest(fCallback, 'IdentityUpdate', { 'Id': sId, 'Email': sEmail, 'Name': sName, 'ReplyTo': sReplyTo, - 'Bcc': sBcc + 'Bcc': sBcc, + 'Signature': sSignature, + 'SignatureInsertBefore': bSignatureInsertBefore ? '1' : '0' }); }; @@ -265,6 +272,36 @@ this.defaultRequest(fCallback, 'Filters', {}); }; + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.templates = function (fCallback) + { + this.defaultRequest(fCallback, 'Templates', {}); + }; + + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.templateGetById = function (fCallback, sID) + { + this.defaultRequest(fCallback, 'TemplateGetByID', { + 'ID': sID + }); + }; + + /** + * @param {?Function} fCallback + */ + RemoteUserStorage.prototype.templateGetById = function (fCallback, sID, sName, sBody) + { + this.defaultRequest(fCallback, 'TemplateSetup', { + 'ID': sID, + 'Name': sName, + 'Body': sBody + }); + }; + /** * @param {?Function} fCallback * @param {string} sFolderFullNameRaw diff --git a/dev/Stores/User/Account.js b/dev/Stores/User/Account.js index f0d0ec0de..d562ccde6 100644 --- a/dev/Stores/User/Account.js +++ b/dev/Stores/User/Account.js @@ -7,9 +7,7 @@ _ = require('_'), ko = require('ko'), - Settings = require('Storage/Settings'), - - Remote = require('Storage/User/Remote') + Settings = require('Storage/Settings') ; /** @@ -26,24 +24,10 @@ this.accounts = ko.observableArray([]); this.accounts.loading = ko.observable(false).extend({'throttle': 100}); - this.accountsEmailNames = ko.observableArray([]).extend({'throttle': 1000}); - this.accountsEmailNames.skipFirst = true; - - this.accounts.subscribe(function (aList) { - this.accountsEmailNames(_.compact(_.map(aList, function (oItem) { + this.accountsEmails = ko.computed(function () { + return _.compact(_.map(this.accounts(), function (oItem) { return oItem ? oItem.email : null; - }))); - }, this); - - this.accountsEmailNames.subscribe(function (aList) { - if (this.accountsEmailNames.skipFirst) - { - this.accountsEmailNames.skipFirst = false; - } - else if (aList && 1 < aList.length) - { - Remote.accountSortOrder(null, aList); - } + })); }, this); this.accountsUnreadCount = ko.computed(function () { @@ -65,10 +49,6 @@ AccountUserStore.prototype.populate = function () { this.email(Settings.settingsGet('Email')); -// this.incLogin(Settings.settingsGet('IncLogin')); -// this.outLogin(Settings.settingsGet('OutLogin')); - -// this.signature(Settings.settingsGet('Signature')); }; module.exports = new AccountUserStore(); diff --git a/dev/Stores/User/Identity.js b/dev/Stores/User/Identity.js index 4fb95ba71..1f0a2acc8 100644 --- a/dev/Stores/User/Identity.js +++ b/dev/Stores/User/Identity.js @@ -4,6 +4,7 @@ 'use strict'; var + _ = require('_'), ko = require('ko') ; @@ -14,6 +15,12 @@ { this.identities = ko.observableArray([]); this.identities.loading = ko.observable(false).extend({'throttle': 100}); + + this.identitiesIDS = ko.computed(function () { + return _.compact(_.map(this.identities(), function (oItem) { + return oItem ? oItem.id : null; + })); + }, this); } module.exports = new IdentityUserStore(); diff --git a/dev/Stores/User/Template.js b/dev/Stores/User/Template.js new file mode 100644 index 000000000..8a3f41030 --- /dev/null +++ b/dev/Stores/User/Template.js @@ -0,0 +1,44 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + ko = require('ko'), + + Remote = require('Storage/User/Remote') + ; + + /** + * @constructor + */ + function TemplateUserStore() + { + this.templates = ko.observableArray([]); + this.templates.loading = ko.observable(false).extend({'throttle': 100}); + + this.templatesNames = ko.observableArray([]).extend({'throttle': 1000}); + this.templatesNames.skipFirst = true; + + this.templates.subscribe(function (aList) { + this.templatesNames(_.compact(_.map(aList, function (oItem) { + return oItem ? oItem.name : null; + }))); + }, this); + + this.templatesNames.subscribe(function (aList) { + if (this.templatesNames.skipFirst) + { + this.templatesNames.skipFirst = false; + } + else if (aList && 1 < aList.length) + { + Remote.templatesSortOrder(null, aList); + } + }, this); + } + + module.exports = new TemplateUserStore(); + +}()); diff --git a/dev/Styles/@Main.less b/dev/Styles/@Main.less index 9ad3e3c95..e6fe214f2 100644 --- a/dev/Styles/@Main.less +++ b/dev/Styles/@Main.less @@ -63,6 +63,7 @@ @import "Filter.less"; @import "Languages.less"; @import "Account.less"; +@import "Template.less"; @import "OpenPgpKey.less"; @import "TwoFactor.less"; @import "Identity.less"; @@ -85,6 +86,7 @@ @import "Settings.less"; @import "SettingsGeneral.less"; @import "SettingsAccounts.less"; +@import "SettingsTemplates.less"; @import "SettingsOpenPGP.less"; @import "SettingsFolders.less"; @import "SettingsThemes.less"; diff --git a/dev/Styles/Compose.less b/dev/Styles/Compose.less index 6ec64376c..94106d0ae 100644 --- a/dev/Styles/Compose.less +++ b/dev/Styles/Compose.less @@ -52,9 +52,10 @@ background-color: #333; background-color: rgba(0,0,0,0.8) !important; - .close { - color: #fff; + .close, .close-custom, .minimize-custom { opacity: 1; + color: #fff; + border-color: #eee; } .btn.disabled { @@ -157,7 +158,7 @@ .error { color: red; } - .close { + .close, .close-custom { float: left; padding-right: 13px; } diff --git a/dev/Styles/Contacts.less b/dev/Styles/Contacts.less index c5d1176b1..02b27013f 100644 --- a/dev/Styles/Contacts.less +++ b/dev/Styles/Contacts.less @@ -41,7 +41,7 @@ background-color: #333; background-color: rgba(0,0,0,0.8) !important; - .close { + .close, .close-custom { color: #fff; opacity: 1; } diff --git a/dev/Styles/Identity.less b/dev/Styles/Identity.less index 5de93b5d9..422f59fac 100644 --- a/dev/Styles/Identity.less +++ b/dev/Styles/Identity.less @@ -1,9 +1,30 @@ .popups { .b-identity-content { + + &.modal { + width: 750px; + } + .modal-header { background-color: #fff; } + .modal-body { + overflow: hidden; + } + + .control-label { + width: 100px; + } + + .controls { + margin-left: 120px; + } + + .e-signature-place { + height: 300px; + } + .textEmail { margin-top: 5px; font-weight: bold; diff --git a/dev/Styles/MessageList.less b/dev/Styles/MessageList.less index 7131f3f4c..8ea5707a2 100644 --- a/dev/Styles/MessageList.less +++ b/dev/Styles/MessageList.less @@ -249,6 +249,13 @@ html.rl-no-preview-pane { margin-right:5px } + &.deleted-mark { + opacity: .7; + .sender, .subject, .subject-prefix, .subject-suffix { + text-decoration: line-through; + } + } + &.important .importantMark { display: inline; } diff --git a/dev/Styles/SettingsTemplates.less b/dev/Styles/SettingsTemplates.less new file mode 100644 index 000000000..85718eba1 --- /dev/null +++ b/dev/Styles/SettingsTemplates.less @@ -0,0 +1,70 @@ + +.b-settings-identities { + + .process-place { + text-align: center; + width: 600px; + padding: 14px 0; + } + + .list-table { + + width: 600px; + + td { + padding: 4px 8px; + line-height: 30px; + } + + .drag-handle { + color: #eee; + } + + tr:hover .drag-handle { + color: #aaa; + } + + .template-img { + font-size: 12px; + margin-right: 5px; + } + + .template-name { + display: inline-block; + word-break: break-all; + box-sizing: border-box; + line-height: 22px; + } + } + + .template-item { + + .e-action { + cursor: pointer; + } + + .drag-handle { + cursor: pointer; + } + + .button-delete { + margin-right: 15px; + margin-top: 5px; + visibility: hidden; + opacity: 0; + } + + .delete-access { + &.button-delete { + visibility: visible; + margin-right: 0; + opacity: 1; + } + } + + .delete-template { + cursor: pointer; + opacity: 0.5; + } + } +} \ No newline at end of file diff --git a/dev/Styles/Template.less b/dev/Styles/Template.less new file mode 100644 index 000000000..6ab167d76 --- /dev/null +++ b/dev/Styles/Template.less @@ -0,0 +1,12 @@ +.popups { + .b-template-add-content { + + &.modal { + width: 700px; + } + + .modal-header { + background-color: #fff; + } + } +} diff --git a/dev/Styles/_BootstrapFix.less b/dev/Styles/_BootstrapFix.less index 492118778..2c005356a 100644 --- a/dev/Styles/_BootstrapFix.less +++ b/dev/Styles/_BootstrapFix.less @@ -11,6 +11,32 @@ label.inline { display: inline-block; } +.close-custom { + .close; +} + +button.close-custom { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} + +.minimize-custom { + border: 0px solid #333; + border-bottom-width: 3px; + display: inline-block; + float: right; + height: 20px; + width: 16px; + font-size: 20px; + font-weight: bold; + line-height: 20px; + margin-right: 15px; + cursor: pointer; +} + .legend { display: block; width: 100%; diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index dea6dd3ad..2cfce8863 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -33,7 +33,6 @@ Data = require('Storage/User/Data'), Cache = require('Storage/User/Cache'), Remote = require('Storage/User/Remote'), - Local = require('Storage/Client'), ComposeAttachmentModel = require('Model/ComposeAttachment'), @@ -74,6 +73,7 @@ } ; + this.oLastMessage = null; this.oEditor = null; this.aDraftInfo = null; this.sInReplyTo = ''; @@ -198,49 +198,19 @@ this.composeEditorArea = ko.observable(null); - this.identities = AccountStore.identities; + this.identities = IdentityStore.identities; this.identitiesOptions = ko.computed(function () { return _.map(IdentityStore.identities(), function (oItem) { return { + 'item': oItem, 'optValue': oItem.id(), 'optText': oItem.formattedName() }; }); }, this); - this.currentIdentityID = ko.observable( - Local.get(Enums.ClientSideKeyName.ComposeLastIdentityID, '')); - - this.currentIdentity = ko.computed(function () { - - var - oItem = null, - sID = this.currentIdentityID(), - aList = IdentityStore.identities() - ; - - if (Utils.isArray(aList)) - { - oItem = _.find(aList, function (oItem) { - return oItem && sID === oItem.id(); - }); - - if (!oItem) - { - oItem = _.find(aList, function (oItem) { - return oItem && '' === oItem.id(); - }); - } - - if (!oItem) - { - oItem = aList[0] ? aList[0] : null; - } - } - - return oItem ? oItem : null; - - }, this); + this.currentIdentity = ko.observable( + this.identities()[0] ? this.identities()[0] : null); this.currentIdentity.extend({'toggleSubscribe': [this, function (oIdentity) { @@ -361,7 +331,7 @@ Remote.sendMessage( this.sendMessageResponse, - this.currentIdentityID(), + this.currentIdentity() ? this.currentIdentity().id() : '', this.draftFolder(), this.draftUid(), sSentFolder, @@ -371,7 +341,7 @@ this.replyTo(), this.subject(), this.oEditor ? this.oEditor.isHtml() : false, - this.oEditor ? this.oEditor.getData(true) : '', + this.oEditor ? this.oEditor.getData(true, true) : '', this.prepearAttachmentsForSendOrSave(), this.aDraftInfo, this.sInReplyTo, @@ -400,7 +370,7 @@ Remote.saveMessage( this.saveMessageResponse, - this.currentIdentityID(), + this.currentIdentity() ? this.currentIdentity().id() : '', this.draftFolder(), this.draftUid(), FolderStore.draftFolder(), @@ -597,7 +567,6 @@ ComposePopupView.prototype.findIdentityByMessage = function (sComposeType, oMessage) { var - sDefaultIdentityID = Local.get(Enums.ClientSideKeyName.ComposeLastIdentityID, ''), aIdentities = IdentityStore.identities(), oResultIdentity = null, oIdentitiesCache = {}, @@ -640,34 +609,15 @@ } } - if (!oResultIdentity) - { - oResultIdentity = _.find(aIdentities, function (oItem) { - return oItem.id() === sDefaultIdentityID; - }); - } - - if (!oResultIdentity && '' !== sDefaultIdentityID) - { - oResultIdentity = _.find(aIdentities, function (oItem) { - return oItem.id() === ''; - }); - } - - if (!oResultIdentity) - { - oResultIdentity = aIdentities[0] || null; - } - - return oResultIdentity; + return oResultIdentity || aIdentities[0] || null; }; ComposePopupView.prototype.selectIdentity = function (oIdentity) { - if (oIdentity) + if (oIdentity && oIdentity.item) { - this.currentIdentityID(oIdentity.optValue); - Local.set(Enums.ClientSideKeyName.ComposeLastIdentityID, oIdentity.optValue); + this.currentIdentity(oIdentity.item); + this.setSignatureFromIdentity(oIdentity.item); } }; @@ -775,97 +725,6 @@ kn.routeOn(); }; - /** - * @param {string} sInputText - * @param {string} sSignature - * @param {string=} sFrom - * @param {string=} sComposeType - * @return {string} - */ - ComposePopupView.prototype.convertSignature = function (sInputText, sSignature, sFrom, sComposeType) - { - var bHtml = false, bData = false; - if ('' !== sSignature) - { - if (':HTML:' === sSignature.substr(0, 6)) - { - bHtml = true; - sSignature = sSignature.substr(6); - } - - sSignature = sSignature.replace(/[\r]/g, ''); - - sFrom = Utils.pString(sFrom); - if ('' !== sFrom) - { - sSignature = sSignature.replace(/{{FROM-FULL}}/g, sFrom); - - if (-1 === sFrom.indexOf(' ') && 0 < sFrom.indexOf('@')) - { - sFrom = sFrom.replace(/@(.+)$/, ''); - } - - sSignature = sSignature.replace(/{{FROM}}/g, sFrom); - } - - sSignature = sSignature.replace(/[\s]{1,2}{{FROM}}/g, '{{FROM}}'); - sSignature = sSignature.replace(/[\s]{1,2}{{FROM-FULL}}/g, '{{FROM-FULL}}'); - - sSignature = sSignature.replace(/{{FROM}}/g, ''); - sSignature = sSignature.replace(/{{FROM-FULL}}/g, ''); - - if (-1 < sSignature.indexOf('{{DATE}}')) - { - sSignature = sSignature.replace(/{{DATE}}/g, moment().format('llll')); - } - - // Enums.ComposeType.Empty === sComposeType - if (-1 < sSignature.indexOf('{{BODY}}')) - { - if (sInputText) - { - bData = true; - - sSignature = bHtml ? sSignature : Utils.plainToHtml(sSignature, true); - sSignature = sSignature.replace('{{BODY}}', sInputText); - } - else - { - sSignature = sSignature.replace(/[\n]?{{BODY}}[\n]?/g, '{{BODY}}'); - sSignature = sSignature.replace(/{{BODY}}/g, ''); - - sSignature = bHtml ? sSignature : Utils.plainToHtml(sSignature, true); - } - } - else - { - sSignature = bHtml ? sSignature : Utils.plainToHtml(sSignature, true); - } - } - - if (!bData) - { - if (sSignature) - { - switch (sComposeType) - { - case Enums.ComposeType.Empty: - sInputText = sInputText + '
' + sSignature; - break; - default: - sInputText = sSignature + '
' + sInputText; - break; - } - } - } - else - { - sInputText = sSignature; - } - - return sInputText; - }; - ComposePopupView.prototype.editor = function (fOnInit) { if (fOnInit) @@ -873,13 +732,11 @@ var self = this; if (!this.oEditor && this.composeEditorArea()) { - _.delay(function () { - self.oEditor = new HtmlEditor(self.composeEditorArea(), null, function () { - fOnInit(self.oEditor); - }, function (bHtml) { - self.isHtml(!!bHtml); - }); - }, 300); + self.oEditor = new HtmlEditor(self.composeEditorArea(), null, function () { + fOnInit(self.oEditor); + }, function (bHtml) { + self.isHtml(!!bHtml); + }); } else if (this.oEditor) { @@ -888,6 +745,101 @@ } }; + ComposePopupView.prototype.converSignature = function (sSignature) + { + var + iLimit = 10, + oMatch = null, + aMoments = [], + oMomentRegx = /{{MOMENT:([^}]+)}}/g, + sFrom = '' + ; + + sSignature = sSignature.replace(/[\r]/g, ''); + + sFrom = this.oLastMessage ? this.emailArrayToStringLineHelper(this.oLastMessage.from, true) : ''; + if ('' !== sFrom) + { + sSignature = sSignature.replace(/{{FROM-FULL}}/g, sFrom); + + if (-1 === sFrom.indexOf(' ') && 0 < sFrom.indexOf('@')) + { + sFrom = sFrom.replace(/@[\S]+/, ''); + } + + sSignature = sSignature.replace(/{{FROM}}/g, sFrom); + } + + sSignature = sSignature.replace(/[\s]{1,2}{{FROM}}/g, '{{FROM}}'); + sSignature = sSignature.replace(/[\s]{1,2}{{FROM-FULL}}/g, '{{FROM-FULL}}'); + + sSignature = sSignature.replace(/{{FROM}}/g, ''); + sSignature = sSignature.replace(/{{FROM-FULL}}/g, ''); + + if (-1 < sSignature.indexOf('{{DATE}}')) + { + sSignature = sSignature.replace(/{{DATE}}/g, moment().format('llll')); + } + + if (-1 < sSignature.indexOf('{{TIME}}')) + { + sSignature = sSignature.replace(/{{TIME}}/g, moment().format('LT')); + } + if (-1 < sSignature.indexOf('{{MOMENT:')) + { + try + { + while ((oMatch = oMomentRegx.exec(sSignature)) !== null) + { + if (oMatch && oMatch[0] && oMatch[1]) + { + aMoments.push([oMatch[0], oMatch[1]]); + } + + iLimit--; + if (0 === iLimit) + { + break; + } + } + + if (aMoments && 0 < aMoments.length) + { + _.each(aMoments, function (aData) { + sSignature = sSignature.replace(aData[0], moment().format(aData[1])); + }); + } + + sSignature = sSignature.replace(/{{MOMENT:[^}]+}}/g, ''); + } + catch(e) {} + } + + return sSignature; + }; + + ComposePopupView.prototype.setSignatureFromIdentity = function (oIdentity) + { + if (oIdentity) + { + var self = this; + this.editor(function (oEditor) { + var bHtml = false, sSignature = oIdentity.signature(); + if ('' !== sSignature) + { + if (':HTML:' === sSignature.substr(0, 6)) + { + bHtml = true; + sSignature = sSignature.substr(6); + } + } + + oEditor.setSignature(self.converSignature(sSignature), + bHtml, !!oIdentity.signatureInsertBefore()); + }); + } + }; + /** * @param {string=} sType = Enums.ComposeType.Empty * @param {?MessageModel|Array=} oMessageOrArray = null @@ -956,6 +908,28 @@ } }; + /** + * + * @param {Array} aList + * @param {boolean} bFriendly + * @returns {string} + */ + ComposePopupView.prototype.emailArrayToStringLineHelper = function (aList, bFriendly) + { + var + iIndex = 0, + iLen = aList.length, + aResult = [] + ; + + for (; iIndex < iLen; iIndex++) + { + aResult.push(aList[iIndex].toLine(!!bFriendly)); + } + + return aResult.join(', '); + }; + /** * @param {string=} sType = Enums.ComposeType.Empty * @param {?MessageModel|Array=} oMessageOrArray = null @@ -984,26 +958,10 @@ oExcludeEmail = {}, oIdentity = null, mEmail = AccountStore.email(), - sSignature = AccountStore.signature(), aDownloads = [], aDraftInfo = null, oMessage = null, - sComposeType = sType || Enums.ComposeType.Empty, - fEmailArrayToStringLineHelper = function (aList, bFriendly) { - - var - iIndex = 0, - iLen = aList.length, - aResult = [] - ; - - for (; iIndex < iLen; iIndex++) - { - aResult.push(aList[iIndex].toLine(!!bFriendly)); - } - - return aResult.join(', '); - } + sComposeType = sType || Enums.ComposeType.Empty ; oMessageOrArray = oMessageOrArray || null; @@ -1013,39 +971,34 @@ (!Utils.isArray(oMessageOrArray) ? oMessageOrArray : null); } + this.oLastMessage = oMessage; + if (null !== mEmail) { oExcludeEmail[mEmail] = true; } + this.reset(); + oIdentity = this.findIdentityByMessage(sComposeType, oMessage); if (oIdentity) { oExcludeEmail[oIdentity.email()] = true; - this.currentIdentityID(oIdentity.id()); } - else - { - this.currentIdentityID(''); - } - - this.reset(); - - this.currentIdentityID.valueHasMutated(); // Populate BBC from Identity if (Utils.isNonEmptyArray(aToEmails)) { - this.to(fEmailArrayToStringLineHelper(aToEmails)); + this.to(this.emailArrayToStringLineHelper(aToEmails)); } if (Utils.isNonEmptyArray(aCcEmails)) { - this.cc(fEmailArrayToStringLineHelper(aCcEmails)); + this.cc(this.emailArrayToStringLineHelper(aCcEmails)); } if (Utils.isNonEmptyArray(aBccEmails)) { - this.bcc(fEmailArrayToStringLineHelper(aBccEmails)); + this.bcc(this.emailArrayToStringLineHelper(aBccEmails)); } if ('' !== sComposeType && oMessage) @@ -1074,7 +1027,7 @@ break; case Enums.ComposeType.Reply: - this.to(fEmailArrayToStringLineHelper(oMessage.replyEmails(oExcludeEmail))); + this.to(this.emailArrayToStringLineHelper(oMessage.replyEmails(oExcludeEmail))); this.subject(Utils.replySubjectAdd('Re', sSubject)); this.prepearMessageAttachments(oMessage, sComposeType); this.aDraftInfo = ['reply', oMessage.uid, oMessage.folderFullNameRaw]; @@ -1084,8 +1037,8 @@ case Enums.ComposeType.ReplyAll: aResplyAllParts = oMessage.replyAllEmails(oExcludeEmail); - this.to(fEmailArrayToStringLineHelper(aResplyAllParts[0])); - this.cc(fEmailArrayToStringLineHelper(aResplyAllParts[1])); + this.to(this.emailArrayToStringLineHelper(aResplyAllParts[0])); + this.cc(this.emailArrayToStringLineHelper(aResplyAllParts[1])); this.subject(Utils.replySubjectAdd('Re', sSubject)); this.prepearMessageAttachments(oMessage, sComposeType); this.aDraftInfo = ['reply', oMessage.uid, oMessage.folderFullNameRaw]; @@ -1110,10 +1063,10 @@ break; case Enums.ComposeType.Draft: - this.to(fEmailArrayToStringLineHelper(oMessage.to)); - this.cc(fEmailArrayToStringLineHelper(oMessage.cc)); - this.bcc(fEmailArrayToStringLineHelper(oMessage.bcc)); - this.replyTo(fEmailArrayToStringLineHelper(oMessage.replyTo)); + this.to(this.emailArrayToStringLineHelper(oMessage.to)); + this.cc(this.emailArrayToStringLineHelper(oMessage.cc)); + this.bcc(this.emailArrayToStringLineHelper(oMessage.bcc)); + this.replyTo(this.emailArrayToStringLineHelper(oMessage.replyTo)); this.bFromDraft = true; @@ -1129,10 +1082,10 @@ break; case Enums.ComposeType.EditAsNew: - this.to(fEmailArrayToStringLineHelper(oMessage.to)); - this.cc(fEmailArrayToStringLineHelper(oMessage.cc)); - this.bcc(fEmailArrayToStringLineHelper(oMessage.bcc)); - this.replyTo(fEmailArrayToStringLineHelper(oMessage.replyTo)); + this.to(this.emailArrayToStringLineHelper(oMessage.to)); + this.cc(this.emailArrayToStringLineHelper(oMessage.cc)); + this.bcc(this.emailArrayToStringLineHelper(oMessage.bcc)); + this.replyTo(this.emailArrayToStringLineHelper(oMessage.replyTo)); this.subject(sSubject); this.prepearMessageAttachments(oMessage, sComposeType); @@ -1153,7 +1106,7 @@ 'EMAIL': sFrom }); - sText = '

' + sReplyTitle + ':' + + sText = '
' + sReplyTitle + ':' + '

' + sText + '

'; break; @@ -1162,7 +1115,7 @@ sFrom = oMessage.fromToLine(false, true); sTo = oMessage.toToLine(false, true); sCc = oMessage.ccToLine(false, true); - sText = '


' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_TITLE') + + sText = '

' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_TITLE') + '
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_FROM') + ': ' + sFrom + '
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_TO') + ': ' + sTo + (0 < sCc.length ? '
' + Translator.i18n('COMPOSE/FORWARD_MESSAGE_TOP_CC') + ': ' + sCc : '') + @@ -1175,19 +1128,20 @@ break; } - if ('' !== sSignature && - Enums.ComposeType.EditAsNew !== sComposeType && Enums.ComposeType.Draft !== sComposeType) - { - sText = this.convertSignature(sText, sSignature, fEmailArrayToStringLineHelper(oMessage.from, true), sComposeType); - } - this.editor(function (oEditor) { + oEditor.setHtml(sText, false); + if (Enums.EditorDefaultType.PlainForced === self.editorDefaultType() || (!oMessage.isHtml() && Enums.EditorDefaultType.HtmlForced !== self.editorDefaultType())) { oEditor.modeToggle(false); } + + if (oIdentity) + { + self.setSignatureFromIdentity(oIdentity); + } }); } else if (Enums.ComposeType.Empty === sComposeType) @@ -1195,18 +1149,21 @@ this.subject(Utils.isNormal(sCustomSubject) ? '' + sCustomSubject : ''); sText = Utils.isNormal(sCustomPlainText) ? '' + sCustomPlainText : ''; - if ('' !== sSignature) - { - sText = this.convertSignature(Utils.plainToHtml(sText, true), sSignature, '', sComposeType); - } this.editor(function (oEditor) { + oEditor.setHtml(sText, false); + if (Enums.EditorDefaultType.Html !== self.editorDefaultType() && Enums.EditorDefaultType.HtmlForced !== self.editorDefaultType()) { oEditor.modeToggle(false); } + + if (oIdentity) + { + self.setSignatureFromIdentity(oIdentity); + } }); } else if (Utils.isNonEmptyArray(oMessageOrArray)) @@ -1214,6 +1171,22 @@ _.each(oMessageOrArray, function (oMessage) { self.addMessageAsAttachment(oMessage); }); + + this.editor(function (oEditor) { + + oEditor.setHtml('', false); + + if (Enums.EditorDefaultType.Html !== self.editorDefaultType() && + Enums.EditorDefaultType.HtmlForced !== self.editorDefaultType()) + { + oEditor.modeToggle(false); + } + + if (oIdentity) + { + self.setSignatureFromIdentity(oIdentity); + } + }); } aDownloads = this.getAttachmentsDownloadsForUpload(); @@ -1252,6 +1225,11 @@ }, aDownloads); } + if (oIdentity) + { + this.currentIdentity(oIdentity); + } + this.triggerForResize(); }; diff --git a/dev/View/Popup/Identity.js b/dev/View/Popup/Identity.js index a7c543929..a513dcb34 100644 --- a/dev/View/Popup/Identity.js +++ b/dev/View/Popup/Identity.js @@ -26,6 +26,8 @@ { AbstractView.call(this, 'Popups', 'PopupsIdentity'); + var self = this; + this.id = ''; this.edit = ko.observable(false); this.owner = ko.observable(false); @@ -43,16 +45,32 @@ this.bcc.focused = ko.observable(false); this.signature = ko.observable(''); + this.signatureInsertBefore = ko.observable(false); - // this.email.subscribe(function () { - // this.email.hasError(false); - // }, this); + this.showBcc = ko.observable(false); + this.showReplyTo = ko.observable(false); this.submitRequest = ko.observable(false); this.submitError = ko.observable(''); + this.bcc.subscribe(function (aValue) { + if (false === self.showBcc() && 0 < aValue.length) + { + self.showBcc(true); + } + }, this); + + this.replyTo.subscribe(function (aValue) { + if (false === self.showReplyTo() && 0 < aValue.length) + { + self.showReplyTo(true); + } + }, this); + this.addOrEditIdentityCommand = Utils.createCommand(this, function () { + this.populateSignatureFromEditor(); + if (!this.email.hasError()) { this.email.hasError('' === Utils.trim(this.email())); @@ -102,7 +120,8 @@ this.submitError(Translator.getNotification(Enums.Notification.UnknownError)); } - }, this), this.id, this.email(), this.name(), this.replyTo(), this.bcc()); + }, this), this.id, this.email(), this.name(), this.replyTo(), this.bcc(), + this.signature(), this.signatureInsertBefore()); return true; @@ -126,17 +145,22 @@ this.email(''); this.replyTo(''); this.bcc(''); + this.signature(''); + this.signatureInsertBefore(false); this.email.hasError(false); this.replyTo.hasError(false); this.bcc.hasError(false); + this.showBcc(false); + this.showReplyTo(false); + this.submitRequest(false); this.submitError(''); if (this.editor) { - this.editor.clear(false); + this.editor.setPlain('', false); } }; @@ -156,6 +180,8 @@ this.email(oIdentity.email()); this.replyTo(oIdentity.replyTo()); this.bcc(oIdentity.bcc()); + this.signature(oIdentity.signature()); + this.signatureInsertBefore(oIdentity.signatureInsertBefore()); this.owner(this.id === ''); } @@ -185,15 +211,23 @@ } }; + IdentityPopupView.prototype.populateSignatureFromEditor = function () + { + if (this.editor) + { + this.signature( + (this.editor.isHtml() ? ':HTML:' : '') + this.editor.getData() + ); + } + }; + IdentityPopupView.prototype.onFocus = function () { if (!this.editor && this.signatureDom()) { var self = this; this.editor = new HtmlEditor(self.signatureDom(), function () { - self.signature( - (self.editor.isHtml() ? ':HTML:' : '') + self.editor.getData() - ); + self.populateSignatureFromEditor(); }, function () { self.setSignature(self.signature()); }); diff --git a/dev/View/Popup/Template.js b/dev/View/Popup/Template.js new file mode 100644 index 000000000..da43d16a3 --- /dev/null +++ b/dev/View/Popup/Template.js @@ -0,0 +1,179 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + ko = require('ko'), + + Enums = require('Common/Enums'), + Utils = require('Common/Utils'), + Translator = require('Common/Translator'), + HtmlEditor = require('Common/HtmlEditor'), + + Remote = require('Storage/User/Remote'), + + kn = require('Knoin/Knoin'), + AbstractView = require('Knoin/AbstractView') + ; + + /** + * @constructor + * @extends AbstractView + */ + function TemplatePopupView() + { + AbstractView.call(this, 'Popups', 'PopupsTemplate'); + + this.editor = null; + this.signatureDom = ko.observable(null); + + this.id = ko.observable(''); + + this.name = ko.observable(''); + this.name.error = ko.observable(false); + this.name.focus = ko.observable(false); + + this.body = ko.observable(''); + this.body.error = ko.observable(false); + + this.name.subscribe(function () { + this.name.error(false); + }, this); + + this.body.subscribe(function () { + this.body.error(false); + }, this); + + this.submitRequest = ko.observable(false); + this.submitError = ko.observable(''); + + this.addTemplateCommand = Utils.createCommand(this, function () { + + this.name.error('' === Utils.trim(this.name())); + this.body.error('' === Utils.trim(this.body()) || + ':HTML:' === Utils.trim(this.body())); + + if (this.name.error() || this.body.error()) + { + return false; + } + + this.submitRequest(true); + + Remote.templateSetup(_.bind(function (sResult, oData) { + + this.submitRequest(false); + if (Enums.StorageResultType.Success === sResult && oData) + { + if (oData.Result) + { + require('App/User').templates(); + this.cancelCommand(); + } + else if (oData.ErrorCode) + { + this.submitError(Translator.getNotification(oData.ErrorCode)); + } + } + else + { + this.submitError(Translator.getNotification(Enums.Notification.UnknownError)); + } + + }, this), this.id(), this.name(), this.body()); + + return true; + + }, function () { + return !this.submitRequest(); + }); + + kn.constructorEnd(this); + } + + kn.extendAsViewModel(['View/Popup/Template'], TemplatePopupView); + _.extend(TemplatePopupView.prototype, AbstractView.prototype); + + TemplatePopupView.prototype.clearPopup = function () + { + this.id(''); + + this.name(''); + this.name.error(false); + + this.body(''); + this.body.loading(false); + this.body.error(false); + + this.submitRequest(false); + this.submitError(''); + }; + + TemplatePopupView.prototype.editorSetBody = function (sBody) + { + var + self = this, + fEditorSetData = function (sBody) { + if (self.editor) + { + if (':HTML:' === sBody.substr(0, 6)) + { + self.editor.setHtml(sBody.substr(6), false); + } + else + { + self.editor.setPlain(sBody, false); + } + } + } + ; + + if (!this.editor && this.signatureDom()) + { + this.editor = new HtmlEditor(self.signatureDom(), function () { + self.body( + (self.editor.isHtml() ? ':HTML:' : '') + self.editor.getData() + ); + }, function () { + fEditorSetData(sBody); + }); + } + else if (this.editor) + { + fEditorSetData(sBody); + } + }; + + TemplatePopupView.prototype.onShow = function (oTemplate) + { + var self = this; + + this.clearPopup(); + + if (oTemplate && oTemplate.id) + { + this.id(oTemplate.id); + this.name(oTemplate.name); + + this.body.loading(true); + + Remote.templateGetById(function () { + + self.body.loading(false); + + self.editorSetBody(''); + + }, this.id()); + } + }; + + TemplatePopupView.prototype.onFocus = function () + { + this.name.focus(true); + }; + + module.exports = TemplatePopupView; + +}()); \ No newline at end of file diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 847d14dba..8f74fbcb2 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -190,7 +190,7 @@ this.viewDate = ko.observable(''); this.viewSize = ko.observable(''); this.viewMoment = ko.observable(''); - this.viewLineAsCcc = ko.observable(''); + this.viewLineAsCss = ko.observable(''); this.viewViewLink = ko.observable(''); this.viewDownloadLink = ko.observable(''); this.viewUserPic = ko.observable(Consts.DataImages.UserDotPic); @@ -272,7 +272,7 @@ this.viewDate(oMessage.fullFormatDateValue()); this.viewSize(oMessage.friendlySize()); this.viewMoment(oMessage.momentDate()); - this.viewLineAsCcc(oMessage.lineAsCcc()); + this.viewLineAsCss(oMessage.lineAsCss()); this.viewViewLink(oMessage.viewLink()); this.viewDownloadLink(oMessage.downloadLink()); this.viewIsImportant(oMessage.isImportant()); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index 5196bb7b9..7186607b4 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -2098,13 +2098,14 @@ class Actions { $sOrder = $this->StorageProvider()->Get($oAccount, \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, - 'accounts_order' + 'accounts_identities_order' ); $aOrder = empty($sOrder) ? array() : @\json_decode($sOrder, true); - if (\is_array($aAccounts) && \is_array($aOrder) && 0 < \count($aOrder)) + if (isset($aOrder['Accounts']) && \is_array($aOrder['Accounts']) && + 1 < \count($aOrder['Accounts'])) { - $aAccounts = \array_merge(\array_flip($aOrder), $aAccounts); + $aAccounts = \array_merge(\array_flip($aOrder['Accounts']), $aAccounts); } } @@ -2174,6 +2175,24 @@ class Actions \array_unshift($aIdentities, \RainLoop\Model\Identity::NewInstanceFromAccount($oAccount)); } + + if (1 < \count($aIdentities)) + { + $sOrder = $this->StorageProvider()->Get($oAccount, + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, + 'accounts_identities_order' + ); + + $aOrder = empty($sOrder) ? array() : @\json_decode($sOrder, true); + if (isset($aOrder['Identities']) && \is_array($aOrder['Identities']) && + 1 < \count($aOrder['Identities'])) + { + $aList = $aOrder['Identities']; + \usort($aIdentities, function ($a, $b) use ($aList) { + return \array_search($a->Id(), $aList) < \array_search($b->Id(), $aList) ? -1 : 1; + }); + } + } } return $aIdentities; @@ -2483,19 +2502,24 @@ class Actions * * @throws \MailSo\Base\Exceptions\Exception */ - public function DoAccountSortOrder() + public function DoAccountsAndIdentitiesSortOrder() { $oAccount = $this->getAccountFromToken(); - $aList = $this->GetActionParam('Accounts', null); - if (!\is_array($aList) || 2 > \count($aList)) + $aAccounts = $this->GetActionParam('Accounts', null); + $aIdentities = $this->GetActionParam('Identities', null); + + if (!\is_array($aAccounts) || !\is_array($aIdentities)) { return $this->FalseResponse(__FUNCTION__); } return $this->DefaultResponse(__FUNCTION__, $this->StorageProvider()->Put($oAccount, - \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_order', - \json_encode($aList) + \RainLoop\Providers\Storage\Enumerations\StorageType::CONFIG, 'accounts_identities_order', + \json_encode(array( + 'Accounts' => $aAccounts, + 'Identities' => $aIdentities + )) )); } @@ -4952,6 +4976,7 @@ class Actions 'IsSeen' => in_array('\\seen', $aLowerFlags), 'IsFlagged' => in_array('\\flagged', $aLowerFlags), 'IsAnswered' => in_array('\\answered', $aLowerFlags), + 'IsDeleted' => in_array('\\deleted', $aLowerFlags), 'IsForwarded' => 0 < strlen($sForwardedFlag) && in_array(strtolower($sForwardedFlag), $aLowerFlags), 'IsReadReceipt' => 0 < strlen($sReadReceiptFlag) && in_array(strtolower($sReadReceiptFlag), $aLowerFlags) ); @@ -8503,6 +8528,7 @@ class Actions $mResult['IsSeen'] = \in_array('\\seen', $aFlags); $mResult['IsFlagged'] = \in_array('\\flagged', $aFlags); $mResult['IsAnswered'] = \in_array('\\answered', $aFlags); + $mResult['IsDeleted'] = \in_array('\\deleted', $aFlags); $sForwardedFlag = $this->Config()->Get('labs', 'imap_forwarded_flag', ''); $sReadReceiptFlag = $this->Config()->Get('labs', 'imap_read_receipt_flag', ''); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php index 0d10cd6a2..19dd0c661 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Model/Identity.php @@ -29,6 +29,16 @@ class Identity */ private $sBcc; + /** + * @var string + */ + private $sSignature; + + /** + * @var bool + */ + private $bSignatureInsertBefore; + /** * @param string $sId = '' * @param string $sEmail = '' @@ -42,6 +52,8 @@ class Identity $this->sName = ''; $this->sReplyTo = ''; $this->sBcc = ''; + $this->sSignature = ''; + $this->bSignatureInsertBefore = false; } /** @@ -114,6 +126,22 @@ class Identity return $this->sBcc; } + /** + * @return string + */ + public function Signature() + { + return $this->sSignature; + } + + /** + * @return bool + */ + public function SignatureInsertBefore() + { + return $this->bSignatureInsertBefore; + } + /** * @param array $aData * @param bool $bAjax = false @@ -129,6 +157,9 @@ class Identity $this->sName = isset($aData['Name']) ? $aData['Name'] : ''; $this->sReplyTo = !empty($aData['ReplyTo']) ? $aData['ReplyTo'] : ''; $this->sBcc = !empty($aData['Bcc']) ? $aData['Bcc'] : ''; + $this->sSignature = !empty($aData['Signature']) ? $aData['Signature'] : ''; + $this->bSignatureInsertBefore = isset($aData['SignatureInsertBefore']) ? + ($bAjax ? '1' === $aData['SignatureInsertBefore'] : !!$aData['SignatureInsertBefore']) : true; return true; } @@ -148,7 +179,9 @@ class Identity 'Email' => $bAjax ? \MailSo\Base\Utils::IdnToUtf8($this->Email()) : $this->Email(), 'Name' => $this->Name(), 'ReplyTo' => $this->ReplyTo(), - 'Bcc' => $this->Bcc() + 'Bcc' => $this->Bcc(), + 'Signature' => $this->Signature(), + 'SignatureInsertBefore' => $this->SignatureInsertBefore() ); } diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html index e046ed550..24d8a6c2e 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItem.html @@ -1,4 +1,4 @@ -
+
 
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html index d816fdb7b..9bb8dc536 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageListItemNoPreviewPane.html @@ -1,4 +1,4 @@ -
+
 
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html index a3f62142c..fad824d4b 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/MailMessageView.html @@ -258,7 +258,7 @@
-
+
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html index 373e76eb7..ac33f802e 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsCompose.html @@ -11,12 +11,10 @@    - - - - - - + + × + + diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html index 497a53675..5268b4045 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsContacts.html @@ -89,7 +89,7 @@
-
+
 
diff --git a/rainloop/v/0.0.0/app/templates/Views/User/PopupsIdentity.html b/rainloop/v/0.0.0/app/templates/Views/User/PopupsIdentity.html index 3469e2b5f..1ccbc8c3e 100644 --- a/rainloop/v/0.0.0/app/templates/Views/User/PopupsIdentity.html +++ b/rainloop/v/0.0.0/app/templates/Views/User/PopupsIdentity.html @@ -18,7 +18,7 @@
-
@@ -32,27 +32,55 @@
-
-
+
-
-
+
-
+
+
+ + +    + + + + +
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/rainloop/v/0.0.0/langs/en.ini b/rainloop/v/0.0.0/langs/en.ini index 42d082628..8f6d9219a 100644 --- a/rainloop/v/0.0.0/langs/en.ini +++ b/rainloop/v/0.0.0/langs/en.ini @@ -249,6 +249,14 @@ BUTTON_ADD_ACCOUNT = "Add" TITLE_UPDATE_ACCOUNT = "Update Account?" BUTTON_UPDATE_ACCOUNT = "Update" +[POPUPS_ADD_TEMPLATE] +TITLE_ADD_TEMPLATE = "Add Template?" +BUTTON_ADD_TEMPLATE = "Add" +TITLE_UPDATE_TEMPLATE = "Update Template?" +BUTTON_UPDATE_TEMPLATE = "Update" +LABEL_NAME = "Name" +LABEL_TEXT = "Text" + [POPUPS_IDENTITIES] TITLE_ADD_IDENTITY = "Add Identity?" TITLE_UPDATE_IDENTITY = "Update Identity?" @@ -260,6 +268,7 @@ LABEL_REPLY_TO = "Reply-To" LABEL_SIGNATURE = "Signature" LABEL_CC = "Cc" LABEL_BCC = "Bcc" +LABEL_SIGNATURE_INSERT_BEFORE = "Insert this signature before quoted text in replies" [POPUPS_CREATE_FOLDER] TITLE_CREATE_FOLDER = "Create a folder?" @@ -401,6 +410,7 @@ LABEL_ACCOUNTS_NAME = "Accounts" LABEL_IDENTITY_NAME = "Identity" LABEL_IDENTITIES_NAME = "Identities" LABEL_FILTERS_NAME = "Filters" +LABEL_TEMPLATES_NAME = "Templates" LABEL_SECURITY_NAME = "Security" LABEL_SOCIAL_NAME = "Social" LABEL_THEMES_NAME = "Themes" @@ -538,6 +548,13 @@ BUTTON_DELETE = "Delete" LOADING_PROCESS = "Updating..." DELETING_ASK = "Are you sure?" +[SETTINGS_TEMPLATES] +LEGEND_TEMPLATES = "Templates" +BUTTON_ADD_TEMPLATE = "Add a Template" +BUTTON_DELETE = "Delete" +LOADING_PROCESS = "Updating..." +DELETING_ASK = "Are you sure?" + [SETTINGS_IDENTITIES] LEGEND_IDENTITY = "Identity" LEGEND_IDENTITIES = "Additional Identities" diff --git a/rainloop/v/0.0.0/static/ckeditor/plugins/plain/plugin.js b/rainloop/v/0.0.0/static/ckeditor/plugins/plain/plugin.js index 8c80eb76e..dc1bc6f38 100644 --- a/rainloop/v/0.0.0/static/ckeditor/plugins/plain/plugin.js +++ b/rainloop/v/0.0.0/static/ckeditor/plugins/plain/plugin.js @@ -53,6 +53,17 @@ if (editor.elementMode === CKEDITOR.ELEMENT_MODE_INLINE) return; + editor.__plainUtils = { + plainToHtml: function(data) { + return window.rainloop_Utils_plainToHtml ? + window.rainloop_Utils_plainToHtml(data, true) : simplePlainToHtml(data); + }, + htmlToPlain: function(data) { + return window.rainloop_Utils_htmlToPlain ? + window.rainloop_Utils_htmlToPlain(data, true) : simpleHtmlToPlain(data); + } + }; + var plain = CKEDITOR.plugins.plain; editor.addMode('plain', function(callback) { @@ -124,9 +135,7 @@ base: CKEDITOR.editable, proto: { setData: function(data) { - this.setValue(window.rainloop_Utils_htmlToPlain ? - window.rainloop_Utils_htmlToPlain(data) : simpleHtmlToPlain(data)); - + this.setValue(this.editor.__plainUtils.htmlToPlain(data)); this.editor.fire('dataReady'); }, setRawData: function(data) { @@ -134,8 +143,7 @@ this.editor.fire('dataReady'); }, getData: function() { - return window.rainloop_Utils_plainToHtml ? - window.rainloop_Utils_plainToHtml(this.getValue(), true) : simplePlainToHtml(this.getValue()); + return this.editor.__plainUtils.plainToHtml(this.getValue()); }, getRawData: function() { return this.getValue(); diff --git a/rainloop/v/0.0.0/static/ckeditor/plugins/signature/plugin.js b/rainloop/v/0.0.0/static/ckeditor/plugins/signature/plugin.js new file mode 100644 index 000000000..5af980dc2 --- /dev/null +++ b/rainloop/v/0.0.0/static/ckeditor/plugins/signature/plugin.js @@ -0,0 +1,97 @@ + +rl_signature_replacer = function (editor, sText, sSignature, bHtml, bInsertBefore) +{ + var + bEmptyText = '' === $.trim(sText), + sNewLine = (bHtml ? '
' : "\n"), + sS1 = "\u0002\u0002", + sE1 = "\u0003\u0003", + sSS1 = '---1beg1---', + sEE1 = '---1end1---', + sS2 = "\u0004\u0004", + sE2 = "\u0005\u0005", + sSS2 = '---2beg2---', + sEE2 = '---2end2---' + ; + + if (!bEmptyText && bHtml) + { + bEmptyText = '' !== $.trim(editor.__plainUtils.htmlToPlain(sText)); + } + + sText = sText.replace(sS1, sSS1).replace(sS1, sSS1).replace(sS1, sSS1).replace(sS1, sSS1); + sText = sText.replace(sE1, sEE1).replace(sE1, sEE1).replace(sE1, sEE1).replace(sE1, sEE1); + sText = sText.replace(sS2, sSS2).replace(sS2, sSS2).replace(sS2, sSS2).replace(sS2, sSS2); + sText = sText.replace(sE2, sEE2).replace(sE2, sEE2).replace(sE2, sEE2).replace(sE2, sEE2); + + if (!/---1beg1---/gm.test(sText)) + { + sText = sSS1 + sEE1 + sText; + } + + if (!/---2beg2---/gm.test(sText)) + { + sText = sText + sSS2 + sEE2; + } + + if (bInsertBefore) + { + sText = sText.replace(/---1beg1---[\s\S]*---1end1---/gm, sSS1 + sSignature + sNewLine + sEE1); + sText = sText.replace(/---2beg2---[\s\S]*---2end2---/gm, sSS2 + '' + sEE2); + } + else + { + sText = sText.replace(/---1beg1---[\s\S]*---1end1---/gm, sSS1 + '' + sEE1); + sText = sText.replace(/---2beg2---[\s\S]*---2end2---/gm, sSS2 + (bEmptyText ? '' : sNewLine) + sSignature + sEE2); + } + + sText = sText.replace(/---1beg1---/g, sS1); + sText = sText.replace(/---1end1---/g, sE1); + sText = sText.replace(/---2beg2---/g, sS2); + sText = sText.replace(/---2end2---/g, sE2); + + return sText; +}; + +CKEDITOR.plugins.add('signature', { + init: function(editor) { + editor.addCommand('insertSignature', { + modes: { wysiwyg: 1, plain: 1 }, + exec: function (editor, cfg) { + + var + bIsHtml = false, + bInsertBefore = false, + sSignature = '' + ; + + if (cfg) { + bIsHtml = undefined === cfg.isHtml ? false : !!cfg.isHtml; + bInsertBefore = undefined === cfg.insertBefore ? false : !!cfg.insertBefore; + sSignature = undefined === cfg.signature ? '' : cfg.signature; + } + + try { + if ('plain' === editor.mode && editor.__plain && editor.__plainUtils) { + if (bIsHtml && editor.__plainUtils.htmlToPlain) { + sSignature = editor.__plainUtils.htmlToPlain(sSignature); + } + + editor.__plain.setRawData( + rl_signature_replacer(editor, + editor.__plain.getRawData(), sSignature, false, bInsertBefore)); + + } else { + if (!bIsHtml && editor.__plainUtils && editor.__plainUtils.plainToHtml) { + sSignature = editor.__plainUtils.plainToHtml(sSignature); + } + + editor.setData( + rl_signature_replacer(editor, + editor.getData(), sSignature, true, bInsertBefore)); + } + } catch (e) {} + } + }); + } +}); \ No newline at end of file diff --git a/vendors/ckeditor-plugins/plain/plugin.js b/vendors/ckeditor-plugins/plain/plugin.js index 8c80eb76e..dc1bc6f38 100644 --- a/vendors/ckeditor-plugins/plain/plugin.js +++ b/vendors/ckeditor-plugins/plain/plugin.js @@ -53,6 +53,17 @@ if (editor.elementMode === CKEDITOR.ELEMENT_MODE_INLINE) return; + editor.__plainUtils = { + plainToHtml: function(data) { + return window.rainloop_Utils_plainToHtml ? + window.rainloop_Utils_plainToHtml(data, true) : simplePlainToHtml(data); + }, + htmlToPlain: function(data) { + return window.rainloop_Utils_htmlToPlain ? + window.rainloop_Utils_htmlToPlain(data, true) : simpleHtmlToPlain(data); + } + }; + var plain = CKEDITOR.plugins.plain; editor.addMode('plain', function(callback) { @@ -124,9 +135,7 @@ base: CKEDITOR.editable, proto: { setData: function(data) { - this.setValue(window.rainloop_Utils_htmlToPlain ? - window.rainloop_Utils_htmlToPlain(data) : simpleHtmlToPlain(data)); - + this.setValue(this.editor.__plainUtils.htmlToPlain(data)); this.editor.fire('dataReady'); }, setRawData: function(data) { @@ -134,8 +143,7 @@ this.editor.fire('dataReady'); }, getData: function() { - return window.rainloop_Utils_plainToHtml ? - window.rainloop_Utils_plainToHtml(this.getValue(), true) : simplePlainToHtml(this.getValue()); + return this.editor.__plainUtils.plainToHtml(this.getValue()); }, getRawData: function() { return this.getValue(); diff --git a/vendors/ckeditor-plugins/signature/plugin.js b/vendors/ckeditor-plugins/signature/plugin.js new file mode 100644 index 000000000..5af980dc2 --- /dev/null +++ b/vendors/ckeditor-plugins/signature/plugin.js @@ -0,0 +1,97 @@ + +rl_signature_replacer = function (editor, sText, sSignature, bHtml, bInsertBefore) +{ + var + bEmptyText = '' === $.trim(sText), + sNewLine = (bHtml ? '
' : "\n"), + sS1 = "\u0002\u0002", + sE1 = "\u0003\u0003", + sSS1 = '---1beg1---', + sEE1 = '---1end1---', + sS2 = "\u0004\u0004", + sE2 = "\u0005\u0005", + sSS2 = '---2beg2---', + sEE2 = '---2end2---' + ; + + if (!bEmptyText && bHtml) + { + bEmptyText = '' !== $.trim(editor.__plainUtils.htmlToPlain(sText)); + } + + sText = sText.replace(sS1, sSS1).replace(sS1, sSS1).replace(sS1, sSS1).replace(sS1, sSS1); + sText = sText.replace(sE1, sEE1).replace(sE1, sEE1).replace(sE1, sEE1).replace(sE1, sEE1); + sText = sText.replace(sS2, sSS2).replace(sS2, sSS2).replace(sS2, sSS2).replace(sS2, sSS2); + sText = sText.replace(sE2, sEE2).replace(sE2, sEE2).replace(sE2, sEE2).replace(sE2, sEE2); + + if (!/---1beg1---/gm.test(sText)) + { + sText = sSS1 + sEE1 + sText; + } + + if (!/---2beg2---/gm.test(sText)) + { + sText = sText + sSS2 + sEE2; + } + + if (bInsertBefore) + { + sText = sText.replace(/---1beg1---[\s\S]*---1end1---/gm, sSS1 + sSignature + sNewLine + sEE1); + sText = sText.replace(/---2beg2---[\s\S]*---2end2---/gm, sSS2 + '' + sEE2); + } + else + { + sText = sText.replace(/---1beg1---[\s\S]*---1end1---/gm, sSS1 + '' + sEE1); + sText = sText.replace(/---2beg2---[\s\S]*---2end2---/gm, sSS2 + (bEmptyText ? '' : sNewLine) + sSignature + sEE2); + } + + sText = sText.replace(/---1beg1---/g, sS1); + sText = sText.replace(/---1end1---/g, sE1); + sText = sText.replace(/---2beg2---/g, sS2); + sText = sText.replace(/---2end2---/g, sE2); + + return sText; +}; + +CKEDITOR.plugins.add('signature', { + init: function(editor) { + editor.addCommand('insertSignature', { + modes: { wysiwyg: 1, plain: 1 }, + exec: function (editor, cfg) { + + var + bIsHtml = false, + bInsertBefore = false, + sSignature = '' + ; + + if (cfg) { + bIsHtml = undefined === cfg.isHtml ? false : !!cfg.isHtml; + bInsertBefore = undefined === cfg.insertBefore ? false : !!cfg.insertBefore; + sSignature = undefined === cfg.signature ? '' : cfg.signature; + } + + try { + if ('plain' === editor.mode && editor.__plain && editor.__plainUtils) { + if (bIsHtml && editor.__plainUtils.htmlToPlain) { + sSignature = editor.__plainUtils.htmlToPlain(sSignature); + } + + editor.__plain.setRawData( + rl_signature_replacer(editor, + editor.__plain.getRawData(), sSignature, false, bInsertBefore)); + + } else { + if (!bIsHtml && editor.__plainUtils && editor.__plainUtils.plainToHtml) { + sSignature = editor.__plainUtils.plainToHtml(sSignature); + } + + editor.setData( + rl_signature_replacer(editor, + editor.getData(), sSignature, true, bInsertBefore)); + } + } catch (e) {} + } + }); + } +}); \ No newline at end of file