diff --git a/dev/App/Admin.js b/dev/App/Admin.js index 15c10514a..882fec1b7 100644 --- a/dev/App/Admin.js +++ b/dev/App/Admin.js @@ -44,10 +44,10 @@ AdminApp.prototype.reloadDomainList = function () { - Data.domainsLoading(true); + Data.domains.loading(true); Remote.domainList(function (sResult, oData) { - Data.domainsLoading(false); + Data.domains.loading(false); if (Enums.StorageResultType.Success === sResult && oData && oData.Result) { var aList = _.map(oData.Result, function (bEnabled, sName) { @@ -65,10 +65,10 @@ AdminApp.prototype.reloadPluginList = function () { - Data.pluginsLoading(true); + Data.plugins.loading(true); Remote.pluginList(function (sResult, oData) { - Data.pluginsLoading(false); + Data.plugins.loading(false); if (Enums.StorageResultType.Success === sResult && oData && oData.Result) { @@ -87,12 +87,12 @@ AdminApp.prototype.reloadPackagesList = function () { - Data.packagesLoading(true); + Data.packages.loading(true); Data.packagesReal(true); Remote.packagesList(function (sResult, oData) { - Data.packagesLoading(false); + Data.packages.loading(false); if (Enums.StorageResultType.Success === sResult && oData && oData.Result) { diff --git a/dev/App/App.js b/dev/App/App.js index d41da725c..ad795a24d 100644 --- a/dev/App/App.js +++ b/dev/App/App.js @@ -464,6 +464,7 @@ } }); + Utils.delegateRunOnDestroy(Data.openpgpkeys()); Data.openpgpkeys(aKeys); } }; @@ -489,6 +490,7 @@ if (Utils.isArray(oData.Result['Accounts'])) { + Utils.delegateRunOnDestroy(Data.accounts()); Data.accounts(_.map(oData.Result['Accounts'], function (sValue) { return new AccountModel(sValue, sValue !== sParentEmail); })); @@ -496,6 +498,7 @@ if (Utils.isArray(oData.Result['Identities'])) { + Utils.delegateRunOnDestroy(Data.identities()); Data.identities(_.map(oData.Result['Identities'], function (oIdentityData) { var diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 4e4fbdbf1..eeaf37bfd 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -1962,6 +1962,54 @@ $('#rl-head-viewport').attr('content', aContent.join(', ')); }; + /** + * @param {Object} oObject + */ + Utils.disposeOne = function (mPropOrValue, mValue) + { + var mDisposable = mValue || mPropOrValue; + if (mDisposable && typeof mDisposable.dispose === 'function') + { + mDisposable.dispose(); + } + }; + + /** + * @param {Object} oObject + */ + Utils.disposeObject = function (oObject) + { + if (oObject) + { + if (Utils.isArray(oObject.disposables)) + { + _.each(oObject.disposables, Utils.disposeOne); + } + + ko.utils.objectForEach(oObject, Utils.disposeOne); + } + }; + + /** + * @param {Object|Array} mObjectOrObjects + */ + Utils.delegateRunOnDestroy = function (mObjectOrObjects) + { + if (mObjectOrObjects) + { + if (Utils.isArray(mObjectOrObjects)) + { + _.each(mObjectOrObjects, function (oItem) { + Utils.delegateRunOnDestroy(oItem); + }); + } + else if (mObjectOrObjects && mObjectOrObjects.onDestroy) + { + mObjectOrObjects.onDestroy(); + } + } + }; + module.exports = Utils; }()); \ No newline at end of file diff --git a/dev/External/ko.js b/dev/External/ko.js index 064f72d2a..962b52fab 100644 --- a/dev/External/ko.js +++ b/dev/External/ko.js @@ -6,24 +6,48 @@ var window = require('window'), _ = require('_'), - $ = require('$') + $ = require('$'), + + fDisposalTooltipHelper = function (oElement, $oEl, oSubscription) { + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + + if (oSubscription && oSubscription.dispose) + { + oSubscription.dispose(); + } + + if ($oEl) + { + $oEl.off('click.koTooltip'); + + if ($oEl.tooltip) + { + $oEl.tooltip('destroy'); + } + } + + $oEl = null; + }); + } ; ko.bindingHandlers.tooltip = { 'init': function (oElement, fValueAccessor) { var + $oEl = null, + sClass = '', + sPlacement = '', + oSubscription = null, Globals = require('Common/Globals'), Utils = require('Common/Utils') ; if (!Globals.bMobileDevice) { - var - $oEl = $(oElement), - sClass = $oEl.data('tooltip-class') || '', - sPlacement = $oEl.data('tooltip-placement') || 'top' - ; + $oEl = $(oElement); + sClass = $oEl.data('tooltip-class') || ''; + sPlacement = $oEl.data('tooltip-placement') || 'top'; $oEl.tooltip({ 'delay': { @@ -36,15 +60,17 @@ 'trigger': 'hover', 'title': function () { return $oEl.is('.disabled') || Globals.dropdownVisibility() ? '' : '' + - Utils.i18n(ko.utils.unwrapObservable(fValueAccessor())) + ''; + Utils.i18n(ko.unwrap(fValueAccessor())) + ''; } - }).click(function () { + }).on('click.koTooltip', function () { $oEl.tooltip('hide'); }); - Globals.tooltipTrigger.subscribe(function () { + oSubscription = Globals.tooltipTrigger.subscribe(function () { $oEl.tooltip('hide'); }); + + fDisposalTooltipHelper(oElement, $oEl, oSubscription); } } }; @@ -53,7 +79,9 @@ 'init': function (oElement, fValueAccessor) { var Globals = require('Common/Globals'), + $oEl = $(oElement), + oSubscription = null, sClass = $oEl.data('tooltip-class') || '', sPlacement = $oEl.data('tooltip-placement') || 'top' ; @@ -70,13 +98,15 @@ return $oEl.is('.disabled') || Globals.dropdownVisibility() ? '' : '' + fValueAccessor()() + ''; } - }).click(function () { + }).on('click.koTooltip', function () { $oEl.tooltip('hide'); }); - Globals.tooltipTrigger.subscribe(function () { + oSubscription = Globals.tooltipTrigger.subscribe(function () { $oEl.tooltip('hide'); }); + + fDisposalTooltipHelper(oElement, $oEl, oSubscription); } }; @@ -85,6 +115,7 @@ var $oEl = $(oElement), + oSubscription = null, Globals = require('Common/Globals') ; @@ -96,16 +127,18 @@ } }); - $(window.document).click(function () { + $(window.document).on('click', function () { $oEl.tooltip('hide'); }); - Globals.tooltipTrigger.subscribe(function () { + oSubscription = Globals.tooltipTrigger.subscribe(function () { $oEl.tooltip('hide'); }); + + fDisposalTooltipHelper(oElement, $oEl, oSubscription); }, 'update': function (oElement, fValueAccessor) { - var sValue = ko.utils.unwrapObservable(fValueAccessor()); + var sValue = ko.unwrap(fValueAccessor()); if ('' === sValue) { $(oElement).data('tooltip3-data', '').tooltip('hide'); @@ -120,22 +153,28 @@ ko.bindingHandlers.registrateBootstrapDropdown = { 'init': function (oElement) { var Globals = require('Common/Globals'); - Globals.aBootstrapDropdowns.push($(oElement)); + if (Globals && Globals.aBootstrapDropdowns) + { + Globals.aBootstrapDropdowns.push($(oElement)); +// ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { +// // TODO +// }); + } } }; ko.bindingHandlers.openDropdownTrigger = { 'update': function (oElement, fValueAccessor) { - if (ko.utils.unwrapObservable(fValueAccessor())) + if (ko.unwrap(fValueAccessor())) { var - $el = $(oElement), + $oEl = $(oElement), Utils = require('Common/Utils') ; - if (!$el.hasClass('open')) + if (!$oEl.hasClass('open')) { - $el.find('.dropdown-toggle').dropdown('toggle'); + $oEl.find('.dropdown-toggle').dropdown('toggle'); Utils.detectDropdownVisibility(); } @@ -154,7 +193,11 @@ ko.bindingHandlers.popover = { 'init': function (oElement, fValueAccessor) { - $(oElement).popover(ko.utils.unwrapObservable(fValueAccessor())); + $(oElement).popover(ko.unwrap(fValueAccessor())); + + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + $(oElement).popover('destroy'); + }); } }; @@ -163,22 +206,22 @@ var Utils = require('Common/Utils'); if (oElement && oElement.styleSheet && !Utils.isUnd(oElement.styleSheet.cssText)) { - oElement.styleSheet.cssText = ko.utils.unwrapObservable(fValueAccessor()); + oElement.styleSheet.cssText = ko.unwrap(fValueAccessor()); } else { - $(oElement).text(ko.utils.unwrapObservable(fValueAccessor())); + $(oElement).text(ko.unwrap(fValueAccessor())); } }, 'update': function (oElement, fValueAccessor) { var Utils = require('Common/Utils'); if (oElement && oElement.styleSheet && !Utils.isUnd(oElement.styleSheet.cssText)) { - oElement.styleSheet.cssText = ko.utils.unwrapObservable(fValueAccessor()); + oElement.styleSheet.cssText = ko.unwrap(fValueAccessor()); } else { - $(oElement).text(ko.utils.unwrapObservable(fValueAccessor())); + $(oElement).text(ko.unwrap(fValueAccessor())); } } }; @@ -204,31 +247,39 @@ ko.bindingHandlers.onEnter = { 'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) { - $(oElement).on('keypress', function (oEvent) { + $(oElement).on('keypress.koOnEnter', function (oEvent) { if (oEvent && 13 === window.parseInt(oEvent.keyCode, 10)) { $(oElement).trigger('change'); fValueAccessor().call(oViewModel); } }); + + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + $(oElement).off('keypress.koOnEnter'); + }); } }; ko.bindingHandlers.onEsc = { 'init': function (oElement, fValueAccessor, fAllBindingsAccessor, oViewModel) { - $(oElement).on('keypress', function (oEvent) { + $(oElement).on('keypress.koOnEsc', function (oEvent) { if (oEvent && 27 === window.parseInt(oEvent.keyCode, 10)) { $(oElement).trigger('change'); fValueAccessor().call(oViewModel); } }); + + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + $(oElement).off('keypress.koOnEsc'); + }); } }; ko.bindingHandlers.clickOnTrue = { 'update': function (oElement, fValueAccessor) { - if (ko.utils.unwrapObservable(fValueAccessor())) + if (ko.unwrap(fValueAccessor())) { $(oElement).click(); } @@ -245,18 +296,25 @@ $(oElement).toggleClass('fade', !Globals.bMobileDevice).modal({ 'keyboard': false, - 'show': ko.utils.unwrapObservable(fValueAccessor()) + 'show': ko.unwrap(fValueAccessor()) }) - .on('shown', function () { + .on('shown.koModal', function () { Utils.windowResize(); }) - .find('.close').click(function () { + .find('.close').on('click.koModal', function () { fValueAccessor()(false); }); + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + $(oElement) + .off('shown.koModal') + .find('.close') + .off('click.koModal') + ; + }); }, 'update': function (oElement, fValueAccessor) { - $(oElement).modal(ko.utils.unwrapObservable(fValueAccessor()) ? 'show' : 'hide'); + $(oElement).modal(ko.unwrap(fValueAccessor()) ? 'show' : 'hide'); } }; @@ -270,26 +328,26 @@ ko.bindingHandlers.i18nUpdate = { 'update': function (oElement, fValueAccessor) { var Utils = require('Common/Utils'); - ko.utils.unwrapObservable(fValueAccessor()); + ko.unwrap(fValueAccessor()); Utils.i18nToNode(oElement); } }; ko.bindingHandlers.link = { 'update': function (oElement, fValueAccessor) { - $(oElement).attr('href', ko.utils.unwrapObservable(fValueAccessor())); + $(oElement).attr('href', ko.unwrap(fValueAccessor())); } }; ko.bindingHandlers.title = { 'update': function (oElement, fValueAccessor) { - $(oElement).attr('title', ko.utils.unwrapObservable(fValueAccessor())); + $(oElement).attr('title', ko.unwrap(fValueAccessor())); } }; ko.bindingHandlers.textF = { 'init': function (oElement, fValueAccessor) { - $(oElement).text(ko.utils.unwrapObservable(fValueAccessor())); + $(oElement).text(ko.unwrap(fValueAccessor())); } }; @@ -301,7 +359,7 @@ ko.bindingHandlers.initResizeTrigger = { 'init': function (oElement, fValueAccessor) { - var aValues = ko.utils.unwrapObservable(fValueAccessor()); + var aValues = ko.unwrap(fValueAccessor()); $(oElement).css({ 'height': aValues[1], 'min-height': aValues[1] @@ -312,7 +370,7 @@ var Utils = require('Common/Utils'), Globals = require('Common/Globals'), - aValues = ko.utils.unwrapObservable(fValueAccessor()), + aValues = ko.unwrap(fValueAccessor()), iValue = Utils.pInt(aValues[1]), iSize = 0, iOffset = $(oElement).offset().top @@ -338,16 +396,18 @@ ko.bindingHandlers.appendDom = { 'update': function (oElement, fValueAccessor) { - $(oElement).hide().empty().append(ko.utils.unwrapObservable(fValueAccessor())).show(); + $(oElement).hide().empty().append(ko.unwrap(fValueAccessor())).show(); } }; ko.bindingHandlers.draggable = { 'init': function (oElement, fValueAccessor, fAllBindingsAccessor) { + var Globals = require('Common/Globals'), Utils = require('Common/Utils') ; + if (!Globals.bMobileDevice) { var @@ -419,9 +479,16 @@ return fValueAccessor()(oEvent && oEvent.target ? ko.dataFor(oEvent.target) : null); }; - $(oElement).draggable(oConf).on('mousedown', function () { + $(oElement).draggable(oConf).on('mousedown.koDraggable', function () { Utils.removeInFocus(); }); + + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + $(oElement) + .off('mousedown.koDraggable') + .draggable('destroy') + ; + }); } } }; @@ -463,6 +530,10 @@ } $(oElement).droppable(oConf); + + ko.utils.domNodeDisposal.addDisposeCallback(oElement, function () { + $(oElement).droppable('destroy'); + }); } } } @@ -504,7 +575,7 @@ }, 'update': function (oElement, fValueAccessor) { var - mValue = ko.utils.unwrapObservable(fValueAccessor()), + mValue = ko.unwrap(fValueAccessor()), $oEl = $(oElement) ; @@ -619,7 +690,7 @@ $oEl = $(oElement), fAllValueFunc = fAllBindingsAccessor(), fEmailsTagsFilter = fAllValueFunc['emailsTagsFilter'] || null, - sValue = ko.utils.unwrapObservable(fValueAccessor()) + sValue = ko.unwrap(fValueAccessor()) ; if ($oEl.data('EmailsTagsValue') !== sValue) @@ -629,7 +700,7 @@ $oEl.inputosaurus('refresh'); } - if (fEmailsTagsFilter && ko.utils.unwrapObservable(fEmailsTagsFilter)) + if (fEmailsTagsFilter && ko.unwrap(fEmailsTagsFilter)) { $oEl.inputosaurus('focus'); } @@ -678,6 +749,8 @@ } }; + // extenders + ko.extenders.trimmer = function (oTarget) { var @@ -771,6 +844,8 @@ return oTarget; }; + // functions + ko.observable.fn.validateNone = function () { this.hasError = ko.observable(false); diff --git a/dev/Knoin/AbstractModel.js b/dev/Knoin/AbstractModel.js new file mode 100644 index 000000000..cc9a0161b --- /dev/null +++ b/dev/Knoin/AbstractModel.js @@ -0,0 +1,50 @@ + +(function () { + + 'use strict'; + + var + _ = require('_'), + + Utils = require('Common/Utils') + ; + + /** + * @constructor + * + * @param {string} sModelName + */ + function AbstractModel(sModelName) + { + this.sModelName = sModelName || ''; + this.disposables = []; + } + + /** + * @param {Array|Object} mInputValue + */ + AbstractModel.prototype.regDisposables = function (mInputValue) + { + if (Utils.isArray(mInputValue)) + { + _.each(mInputValue, function (mValue) { + this.disposables.push(mValue); + }, this); + } + else if (mInputValue) + { + this.disposables.push(mInputValue); + } + + }; + + AbstractModel.prototype.onDestroy = function () + { + Utils.disposeObject(this); +// window.console.log('onDestroy: ' + this.sModelName); + }; + + module.exports = AbstractModel; + +}()); + diff --git a/dev/Model/Account.js b/dev/Model/Account.js index 9f7aba2f4..3b3ea5ab0 100644 --- a/dev/Model/Account.js +++ b/dev/Model/Account.js @@ -4,9 +4,12 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), - Utils = require('Common/Utils') + Utils = require('Common/Utils'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -17,12 +20,16 @@ */ function AccountModel(sEmail, bCanBeDelete) { + AbstractModel.call(this, 'AccountModel'); + this.email = sEmail; this.deleteAccess = ko.observable(false); this.canBeDalete = ko.observable(Utils.isUnd(bCanBeDelete) ? true : !!bCanBeDelete); } + _.extend(AccountModel.prototype, AbstractModel.prototype); + /** * @type {string} */ diff --git a/dev/Model/Attachment.js b/dev/Model/Attachment.js index adf42549d..26a073426 100644 --- a/dev/Model/Attachment.js +++ b/dev/Model/Attachment.js @@ -5,10 +5,13 @@ var window = require('window'), + _ = require('_'), Globals = require('Common/Globals'), Utils = require('Common/Utils'), - LinkBuilder = require('Common/LinkBuilder') + LinkBuilder = require('Common/LinkBuilder'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -16,6 +19,8 @@ */ function AttachmentModel() { + AbstractModel.call(this, 'AttachmentModel'); + this.mimeType = ''; this.fileName = ''; this.estimatedSize = 0; @@ -31,6 +36,8 @@ this.mimeIndex = ''; } + _.extend(AttachmentModel.prototype, AbstractModel.prototype); + /** * @static * @param {AjaxJsonAttachment} oJsonAttachment diff --git a/dev/Model/ComposeAttachment.js b/dev/Model/ComposeAttachment.js index 346f67413..232be7af5 100644 --- a/dev/Model/ComposeAttachment.js +++ b/dev/Model/ComposeAttachment.js @@ -4,9 +4,12 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), - Utils = require('Common/Utils') + Utils = require('Common/Utils'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -21,6 +24,8 @@ */ function ComposeAttachmentModel(sId, sFileName, nSize, bInline, bLinked, sCID, sContentLocation) { + AbstractModel.call(this, 'ComposeAttachmentModel'); + this.id = sId; this.isInline = Utils.isUnd(bInline) ? false : !!bInline; this.isLinked = Utils.isUnd(bLinked) ? false : !!bLinked; @@ -42,8 +47,12 @@ var mSize = this.size(); return null === mSize ? '' : Utils.friendlySize(this.size()); }, this); + + this.regDisposables([this.friendlySize]); } + _.extend(ComposeAttachmentModel.prototype, AbstractModel.prototype); + ComposeAttachmentModel.prototype.id = ''; ComposeAttachmentModel.prototype.isInline = false; ComposeAttachmentModel.prototype.isLinked = false; diff --git a/dev/Model/Contact.js b/dev/Model/Contact.js index 47bd489c4..8e6de74ae 100644 --- a/dev/Model/Contact.js +++ b/dev/Model/Contact.js @@ -9,7 +9,9 @@ Enums = require('Common/Enums'), Utils = require('Common/Utils'), - LinkBuilder = require('Common/LinkBuilder') + LinkBuilder = require('Common/LinkBuilder'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -17,6 +19,8 @@ */ function ContactModel() { + AbstractModel.call(this, 'ContactModel'); + this.idContact = 0; this.display = ''; this.properties = []; @@ -28,6 +32,8 @@ this.deleted = ko.observable(false); } + _.extend(ContactModel.prototype, AbstractModel.prototype); + /** * @return {Array|null} */ diff --git a/dev/Model/ContactProperty.js b/dev/Model/ContactProperty.js index d7f55ed19..1ea5fc95a 100644 --- a/dev/Model/ContactProperty.js +++ b/dev/Model/ContactProperty.js @@ -4,10 +4,13 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), Enums = require('Common/Enums'), - Utils = require('Common/Utils') + Utils = require('Common/Utils'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -20,6 +23,8 @@ */ function ContactPropertyModel(iType, sTypeStr, sValue, bFocused, sPlaceholder) { + AbstractModel.call(this, 'ContactPropertyModel'); + this.type = ko.observable(Utils.isUnd(iType) ? Enums.ContactPropertyType.Unknown : iType); this.typeStr = ko.observable(Utils.isUnd(sTypeStr) ? '' : sTypeStr); this.focused = ko.observable(Utils.isUnd(bFocused) ? false : !!bFocused); @@ -35,8 +40,12 @@ this.largeValue = ko.computed(function () { return Enums.ContactPropertyType.Note === this.type(); }, this); + + this.regDisposables([this.placeholderValue, this.largeValue]); } + _.extend(ContactPropertyModel.prototype, AbstractModel.prototype); + module.exports = ContactPropertyModel; }()); \ No newline at end of file diff --git a/dev/Model/Filter.js b/dev/Model/Filter.js index 88f6da5d3..d7bf42618 100644 --- a/dev/Model/Filter.js +++ b/dev/Model/Filter.js @@ -4,11 +4,14 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), Enums = require('Common/Enums'), Utils = require('Common/Utils'), - FilterConditionModel = require('Model/FilterCondition') + FilterConditionModel = require('Model/FilterCondition'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -16,6 +19,8 @@ */ function FilterModel() { + AbstractModel.call(this, 'FilterModel'); + this.isNew = ko.observable(true); this.enabled = ko.observable(true); @@ -25,9 +30,9 @@ this.conditions = ko.observableArray([]); - this.conditions.subscribe(function () { + this.regDisposables(this.conditions.subscribe(function () { Utils.windowResize(); - }); + })); // Actions this.actionMarkAsRead = ko.observable(false); @@ -69,8 +74,12 @@ return sTemplate; }, this); + + this.regDisposables([this.actionMarkAsReadVisiblity, this.actionTemplate]); } + _.extend(FilterModel.prototype, AbstractModel.prototype); + FilterModel.prototype.addCondition = function () { this.conditions.push(new FilterConditionModel(this.conditions)); diff --git a/dev/Model/FilterCondition.js b/dev/Model/FilterCondition.js index 7bde5f94a..67dba75de 100644 --- a/dev/Model/FilterCondition.js +++ b/dev/Model/FilterCondition.js @@ -4,9 +4,12 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), - Enums = require('Common/Enums') + Enums = require('Common/Enums'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -15,6 +18,8 @@ */ function FilterConditionModel(oKoList) { + AbstractModel.call(this, 'FilterConditionModel'); + this.parentList = oKoList; this.field = ko.observable(Enums.FilterConditionField.From); @@ -50,8 +55,12 @@ return sTemplate; }, this); + + this.regDisposables([this.template]); } + _.extend(FilterConditionModel.prototype, AbstractModel.prototype); + FilterConditionModel.prototype.removeSelf = function () { this.parentList.remove(this); diff --git a/dev/Model/Folder.js b/dev/Model/Folder.js index f5794ffa7..6dcc9c936 100644 --- a/dev/Model/Folder.js +++ b/dev/Model/Folder.js @@ -10,7 +10,9 @@ Enums = require('Common/Enums'), Globals = require('Common/Globals'), Utils = require('Common/Utils'), - Events = require('Common/Events') + Events = require('Common/Events'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -18,6 +20,8 @@ */ function FolderModel() { + AbstractModel.call(this, 'FolderModel'); + this.name = ko.observable(''); this.fullName = ''; this.fullNameRaw = ''; @@ -49,6 +53,8 @@ this.collapsedPrivate = ko.observable(true); } + _.extend(FolderModel.prototype, AbstractModel.prototype); + /** * @static * @param {AjaxJsonFolder} oJsonFolder diff --git a/dev/Model/Identity.js b/dev/Model/Identity.js index 0a8f512a1..6bed8d838 100644 --- a/dev/Model/Identity.js +++ b/dev/Model/Identity.js @@ -4,9 +4,12 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), - Utils = require('Common/Utils') + Utils = require('Common/Utils'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -17,6 +20,8 @@ */ function IdentityModel(sId, sEmail, bCanBeDelete) { + AbstractModel.call(this, 'IdentityModel'); + this.id = sId; this.email = ko.observable(sEmail); this.name = ko.observable(''); @@ -27,6 +32,8 @@ this.canBeDalete = ko.observable(bCanBeDelete); } + _.extend(IdentityModel.prototype, AbstractModel.prototype); + IdentityModel.prototype.formattedName = function () { var sName = this.name(); diff --git a/dev/Model/Message.js b/dev/Model/Message.js index 7a44ae66f..fedff5614 100644 --- a/dev/Model/Message.js +++ b/dev/Model/Message.js @@ -16,7 +16,9 @@ LinkBuilder = require('Common/LinkBuilder'), EmailModel = require('Model/Email'), - AttachmentModel = require('Model/Attachment') + AttachmentModel = require('Model/Attachment'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -24,6 +26,8 @@ */ function MessageModel() { + AbstractModel.call(this, 'MessageModel'); + this.folderFullNameRaw = ''; this.uid = ''; this.hash = ''; @@ -68,9 +72,9 @@ this.checked = ko.observable(false); this.hasAttachments = ko.observable(false); this.attachmentsMainType = ko.observable(''); - + this.moment = ko.observable(moment(moment.unix(0))); - + this.attachmentIconClass = ko.computed(function () { var sClass = ''; if (this.hasAttachments()) @@ -102,10 +106,10 @@ this.momentDate = Utils.createMomentDate(this); this.momentShortDate = Utils.createMomentShortDate(this); - this.dateTimeStampInUTC.subscribe(function (iValue) { + this.regDisposables(this.dateTimeStampInUTC.subscribe(function (iValue) { var iNow = moment().unix(); this.moment(moment.unix(iNow < iValue ? iNow : iValue)); - }, this); + }, this)); this.body = null; this.plainRaw = ''; @@ -139,8 +143,12 @@ var iCount = this.threadsLen(); return 0 === this.parentUid() && 0 < iCount ? iCount + 1 : ''; }, this); + + this.regDisposables([this.attachmentIconClass, this.fullFormatDateValue, this.threadsLenResult]); } + _.extend(MessageModel.prototype, AbstractModel.prototype); + /** * @static * @param {AjaxJsonMessage} oJsonMessage diff --git a/dev/Model/OpenPgpKey.js b/dev/Model/OpenPgpKey.js index d2b0abfc2..22c46bbbe 100644 --- a/dev/Model/OpenPgpKey.js +++ b/dev/Model/OpenPgpKey.js @@ -4,7 +4,10 @@ 'use strict'; var - ko = require('ko') + _ = require('_'), + ko = require('ko'), + + AbstractModel = require('Knoin/AbstractModel') ; /** @@ -19,6 +22,8 @@ */ function OpenPgpKeyModel(iIndex, sGuID, sID, sUserID, sEmail, bIsPrivate, sArmor) { + AbstractModel.call(this, 'OpenPgpKeyModel'); + this.index = iIndex; this.id = sID; this.guid = sGuID; @@ -30,6 +35,8 @@ this.deleteAccess = ko.observable(false); } + _.extend(OpenPgpKeyModel.prototype, AbstractModel.prototype); + OpenPgpKeyModel.prototype.index = 0; OpenPgpKeyModel.prototype.id = ''; OpenPgpKeyModel.prototype.guid = ''; diff --git a/dev/Settings/Admin/Domains.js b/dev/Settings/Admin/Domains.js index 5bb60970e..c1e98ce1b 100644 --- a/dev/Settings/Admin/Domains.js +++ b/dev/Settings/Admin/Domains.js @@ -22,12 +22,11 @@ function DomainsAdminSetting() { this.domains = Data.domains; - this.domainsLoading = Data.domainsLoading; this.iDomainForDeletionTimeout = 0; this.visibility = ko.computed(function () { - return Data.domainsLoading() ? 'visible' : 'hidden'; + return Data.domains.loading() ? 'visible' : 'hidden'; }, this); this.domainForDeletion = ko.observable(null).extend({'toggleSubscribe': [this, diff --git a/dev/Settings/Admin/Packages.js b/dev/Settings/Admin/Packages.js index 0395083ce..368a318a2 100644 --- a/dev/Settings/Admin/Packages.js +++ b/dev/Settings/Admin/Packages.js @@ -22,7 +22,6 @@ this.packagesError = ko.observable(''); this.packages = Data.packages; - this.packagesLoading = Data.packagesLoading; this.packagesReal = Data.packagesReal; this.packagesMainUpdatable = Data.packagesMainUpdatable; @@ -39,7 +38,7 @@ }); this.visibility = ko.computed(function () { - return Data.packagesLoading() ? 'visible' : 'hidden'; + return Data.packages.loading() ? 'visible' : 'hidden'; }, this); } diff --git a/dev/Settings/Admin/Plugins.js b/dev/Settings/Admin/Plugins.js index 6841ee7d2..e736ae430 100644 --- a/dev/Settings/Admin/Plugins.js +++ b/dev/Settings/Admin/Plugins.js @@ -25,10 +25,9 @@ this.pluginsError = ko.observable(''); this.plugins = Data.plugins; - this.pluginsLoading = Data.pluginsLoading; this.visibility = ko.computed(function () { - return Data.pluginsLoading() ? 'visible' : 'hidden'; + return Data.plugins.loading() ? 'visible' : 'hidden'; }, this); this.onPluginLoadRequest = _.bind(this.onPluginLoadRequest, this); diff --git a/dev/Settings/App/Filters.js b/dev/Settings/App/Filters.js index dde98a067..8847901d5 100644 --- a/dev/Settings/App/Filters.js +++ b/dev/Settings/App/Filters.js @@ -25,6 +25,7 @@ FiltersAppSetting.prototype.deleteFilter = function (oFilter) { this.filters.remove(oFilter); + Utils.delegateRunOnDestroy(oFilter); }; FiltersAppSetting.prototype.addFilter = function () diff --git a/dev/Settings/App/OpenPGP.js b/dev/Settings/App/OpenPGP.js index 143bdea07..5ad3f5b63 100644 --- a/dev/Settings/App/OpenPGP.js +++ b/dev/Settings/App/OpenPGP.js @@ -4,8 +4,11 @@ 'use strict'; var + _ = require('_'), ko = require('ko'), + Utils = require('Common/Utils'), + kn = require('Knoin/Knoin'), Data = require('Storage/App/Data') @@ -64,14 +67,20 @@ if (oOpenPgpKeyToRemove && Data.openpgpKeyring) { - this.openpgpkeys.remove(function (oOpenPgpKey) { + var oFindedItem = _.find(this.openpgpkeys(), function (oOpenPgpKey) { return oOpenPgpKeyToRemove === oOpenPgpKey; }); - Data.openpgpKeyring[oOpenPgpKeyToRemove.isPrivate ? 'privateKeys' : 'publicKeys'] - .removeForId(oOpenPgpKeyToRemove.guid); + if (oFindedItem) + { + this.openpgpkeys.remove(oFindedItem); + Utils.delegateRunOnDestroy(oFindedItem); - Data.openpgpKeyring.store(); + Data.openpgpKeyring[oFindedItem.isPrivate ? 'privateKeys' : 'publicKeys'] + .removeForId(oFindedItem.guid); + + Data.openpgpKeyring.store(); + } require('App/App').reloadOpenPgpKeys(); } diff --git a/dev/Storage/Admin/Data.js b/dev/Storage/Admin/Data.js index 4c1789d3c..710dd4c58 100644 --- a/dev/Storage/Admin/Data.js +++ b/dev/Storage/Admin/Data.js @@ -19,16 +19,16 @@ { AbstractData.call(this); - this.domainsLoading = ko.observable(false).extend({'throttle': 100}); this.domains = ko.observableArray([]); + this.domains.loading = ko.observable(false).extend({'throttle': 100}); - this.pluginsLoading = ko.observable(false).extend({'throttle': 100}); this.plugins = ko.observableArray([]); + this.plugins.loading = ko.observable(false).extend({'throttle': 100}); this.packagesReal = ko.observable(true); this.packagesMainUpdatable = ko.observable(true); - this.packagesLoading = ko.observable(false).extend({'throttle': 100}); this.packages = ko.observableArray([]); + this.packages.loading = ko.observable(false).extend({'throttle': 100}); this.coreReal = ko.observable(true); this.coreUpdatable = ko.observable(true); @@ -48,7 +48,7 @@ this.licenseTrigger = ko.observable(false); this.adminManLoading = ko.computed(function () { - return '000' !== [this.domainsLoading() ? '1' : '0', this.pluginsLoading() ? '1' : '0', this.packagesLoading() ? '1' : '0'].join(''); + return '000' !== [this.domains.loading() ? '1' : '0', this.plugins.loading() ? '1' : '0', this.packagesLoading() ? '1' : '0'].join(''); }, this); this.adminManLoadingVisibility = ko.computed(function () { diff --git a/dev/View/Popup/Compose.js b/dev/View/Popup/Compose.js index 6233641a5..cde75cc48 100644 --- a/dev/View/Popup/Compose.js +++ b/dev/View/Popup/Compose.js @@ -1257,6 +1257,29 @@ return null; }; + ComposePopupView.prototype.cancelAttachmentHelper = function (sId, oJua) { + + var self = this; + return function () { + + var oItem = _.find(self.attachments(), function (oItem) { + return oItem && oItem.id === sId; + }); + + if (oItem) + { + self.attachments.remove(oItem); + Utils.delegateRunOnDestroy(oItem); + + if (oJua) + { + oJua.cancel(sId); + } + } + }; + + }; + ComposePopupView.prototype.initUploader = function () { if (this.composeUploaderButton()) @@ -1325,20 +1348,7 @@ oAttachment = new ComposeAttachmentModel(sId, sFileName, mSize) ; - oAttachment.cancel = (function (sId) { - - return function () { - that.attachments.remove(function (oItem) { - return oItem && oItem.id === sId; - }); - - if (oJua) - { - oJua.cancel(sId); - } - }; - - }(sId)); + oAttachment.cancel = that.cancelAttachmentHelper(sId, oJua); this.attachments.push(oAttachment); @@ -1473,14 +1483,7 @@ var self = this, oAttachment = null, - sTemp = oMessage.subject(), - fCancelFunc = function (sId) { - return function () { - self.attachments.remove(function (oItem) { - return oItem && oItem.id === sId; - }); - }; - } + sTemp = oMessage.subject() ; sTemp = '.eml' === sTemp.substr(-4).toLowerCase() ? sTemp : sTemp + '.eml'; @@ -1489,7 +1492,7 @@ ); oAttachment.fromMessage = true; - oAttachment.cancel = fCancelFunc(oMessage.requestHash); + oAttachment.cancel = this.cancelAttachmentHelper(oMessage.requestHash); oAttachment.waiting(false).uploading(true); this.attachments.push(oAttachment); @@ -1505,13 +1508,6 @@ var self = this, oAttachment = null, - fCancelFunc = function (sId) { - return function () { - self.attachments.remove(function (oItem) { - return oItem && oItem.id === sId; - }); - }; - }, iAttachmentSizeLimit = Utils.pInt(Settings.settingsGet('AttachmentLimit')), mSize = oDropboxFile['bytes'] ; @@ -1521,7 +1517,7 @@ ); oAttachment.fromMessage = false; - oAttachment.cancel = fCancelFunc(oDropboxFile['link']); + oAttachment.cancel = this.cancelAttachmentHelper(oDropboxFile['link']); oAttachment.waiting(false).uploading(true); this.attachments.push(oAttachment); @@ -1566,13 +1562,6 @@ { var self = this, - fCancelFunc = function (sId) { - return function () { - self.attachments.remove(function (oItem) { - return oItem && oItem.id === sId; - }); - }; - }, iAttachmentSizeLimit = Utils.pInt(Settings.settingsGet('AttachmentLimit')), oAttachment = null, mSize = oDriveFile['fileSize'] ? Utils.pInt(oDriveFile['fileSize']) : 0 @@ -1583,7 +1572,7 @@ ); oAttachment.fromMessage = false; - oAttachment.cancel = fCancelFunc(oDriveFile['downloadUrl']); + oAttachment.cancel = this.cancelAttachmentHelper(oDriveFile['downloadUrl']); oAttachment.waiting(false).uploading(true); this.attachments.push(oAttachment); @@ -1629,20 +1618,12 @@ if (oMessage) { var - self = this, aAttachments = Utils.isNonEmptyArray(oMessage.attachments()) ? oMessage.attachments() : [], iIndex = 0, iLen = aAttachments.length, oAttachment = null, oItem = null, - bAdd = false, - fCancelFunc = function (sId) { - return function () { - self.attachments.remove(function (oItem) { - return oItem && oItem.id === sId; - }); - }; - } + bAdd = false ; if (Enums.ComposeType.ForwardAsAttachment === sType) @@ -1677,7 +1658,7 @@ ); oAttachment.fromMessage = true; - oAttachment.cancel = fCancelFunc(oItem.download); + oAttachment.cancel = this.cancelAttachmentHelper(oItem.download); oAttachment.waiting(false).uploading(true); this.attachments.push(oAttachment); @@ -1689,9 +1670,15 @@ ComposePopupView.prototype.removeLinkedAttachments = function () { - this.attachments.remove(function (oItem) { + var oItem = _.find(this.attachments(), function (oItem) { return oItem && oItem.isLinked; }); + + if (oItem) + { + this.attachments.remove(oItem); + Utils.delegateRunOnDestroy(oItem); + } }; ComposePopupView.prototype.setMessageAttachmentFailedDowbloadText = function () @@ -1751,7 +1738,9 @@ this.attachmentsInProcessError(false); this.showCcAndBcc(false); + Utils.delegateRunOnDestroy(this.attachments()); this.attachments([]); + this.dragAndDropOver(false); this.dragAndDropVisible(false); diff --git a/dev/View/Popup/Contacts.js b/dev/View/Popup/Contacts.js index 22bf9b9d0..5dcdd0e98 100644 --- a/dev/View/Popup/Contacts.js +++ b/dev/View/Popup/Contacts.js @@ -41,6 +41,7 @@ fFastClearEmptyListHelper = function (aList) { if (aList && 0 < aList.length) { self.viewProperties.removeAll(aList); + Utils.delegateRunOnDestroy(aList); } } ; @@ -527,6 +528,7 @@ _.each(aContacts, function (oContact) { oKoContacts.remove(oContact); + Utils.delegateRunOnDestroy(oContact); }); }, 500); @@ -569,6 +571,7 @@ ContactsPopupView.prototype.removeProperty = function (oProp) { this.viewProperties.remove(oProp); + Utils.delegateRunOnDestroy(oProp); }; /** @@ -622,6 +625,9 @@ this.getPropertyPlceholder(Enums.ContactPropertyType.FirstName))); this.viewID(sId); + + Utils.delegateRunOnDestroy(this.viewProperties()); + this.viewProperties([]); this.viewProperties(aList); @@ -673,9 +679,10 @@ self.contactsCount(iCount); + Utils.delegateRunOnDestroy(self.contacts()); self.contacts(aList); - self.contacts.loading(false); + self.contacts.loading(false); self.viewClearSearch('' !== self.search()); }, iOffset, Consts.Defaults.ContactsPerPage, this.search()); @@ -722,6 +729,8 @@ this.emptySelection(true); this.search(''); this.contactsCount(0); + + Utils.delegateRunOnDestroy(this.contacts()); this.contacts([]); }; diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html index 6273ba769..fbbe560ca 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminSettingsPackages.html @@ -1,6 +1,6 @@