mirror of
https://github.com/nasa/openmct.git
synced 2025-04-12 13:47:55 +00:00
Merge pull request #1834 from nasa/summary-widgets-ml
Summary widgets Memory Leak Fix
This commit is contained in:
commit
284dec4903
@ -48,6 +48,7 @@ define([
|
||||
this.unlisteners.forEach(function (unlisten) {
|
||||
unlisten();
|
||||
});
|
||||
this.unlisteners = [];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ define([
|
||||
'./input/ObjectSelect',
|
||||
'./input/KeySelect',
|
||||
'./input/OperationSelect',
|
||||
'./eventHelpers',
|
||||
'EventEmitter',
|
||||
'zepto'
|
||||
], function (
|
||||
@ -10,10 +11,10 @@ define([
|
||||
ObjectSelect,
|
||||
KeySelect,
|
||||
OperationSelect,
|
||||
eventHelpers,
|
||||
EventEmitter,
|
||||
$
|
||||
) {
|
||||
|
||||
/**
|
||||
* Represents an individual condition for a summary widget rule. Manages the
|
||||
* associated inputs and view.
|
||||
@ -25,6 +26,7 @@ define([
|
||||
* selects with configuration data
|
||||
*/
|
||||
function Condition(conditionConfig, index, conditionManager) {
|
||||
eventHelpers.extend(this);
|
||||
this.config = conditionConfig;
|
||||
this.index = index;
|
||||
this.conditionManager = conditionManager;
|
||||
@ -71,15 +73,17 @@ define([
|
||||
value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber),
|
||||
inputIndex = self.valueInputs.indexOf(elem);
|
||||
|
||||
self.eventEmitter.emit('change', {
|
||||
value: value,
|
||||
property: 'values[' + inputIndex + ']',
|
||||
index: self.index
|
||||
});
|
||||
if (elem.tagName.toUpperCase() === 'INPUT') {
|
||||
self.eventEmitter.emit('change', {
|
||||
value: value,
|
||||
property: 'values[' + inputIndex + ']',
|
||||
index: self.index
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.deleteButton.on('click', this.remove);
|
||||
this.duplicateButton.on('click', this.duplicate);
|
||||
this.listenTo(this.deleteButton, 'click', this.remove, this);
|
||||
this.listenTo(this.duplicateButton, 'click', this.duplicate, this);
|
||||
|
||||
this.selects.object = new ObjectSelect(this.config, this.conditionManager, [
|
||||
['any', 'any telemetry'],
|
||||
@ -105,7 +109,7 @@ define([
|
||||
$('.t-configuration', self.domElement).append(select.getDOM());
|
||||
});
|
||||
|
||||
$(this.domElement).on('input', 'input', onValueInput);
|
||||
this.listenTo($(this.domElement), 'input', onValueInput);
|
||||
}
|
||||
|
||||
Condition.prototype.getDOM = function (container) {
|
||||
@ -139,6 +143,14 @@ define([
|
||||
*/
|
||||
Condition.prototype.remove = function () {
|
||||
this.eventEmitter.emit('remove', this.index);
|
||||
this.destroy();
|
||||
};
|
||||
|
||||
Condition.prototype.destroy = function () {
|
||||
this.stopListening();
|
||||
Object.values(this.selects).forEach(function (select) {
|
||||
select.destroy();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3,6 +3,7 @@ define([
|
||||
'./Condition',
|
||||
'./input/ColorPalette',
|
||||
'./input/IconPalette',
|
||||
'./eventHelpers',
|
||||
'EventEmitter',
|
||||
'lodash',
|
||||
'zepto'
|
||||
@ -11,11 +12,11 @@ define([
|
||||
Condition,
|
||||
ColorPalette,
|
||||
IconPalette,
|
||||
eventHelpers,
|
||||
EventEmitter,
|
||||
_,
|
||||
$
|
||||
) {
|
||||
|
||||
/**
|
||||
* An object representing a summary widget rule. Maintains a set of text
|
||||
* and css properties for output, and a set of conditions for configuring
|
||||
@ -29,6 +30,7 @@ define([
|
||||
* @param {element} container The DOM element which cotains this summary widget
|
||||
*/
|
||||
function Rule(ruleConfig, domainObject, openmct, conditionManager, widgetDnD, container) {
|
||||
eventHelpers.extend(this);
|
||||
var self = this;
|
||||
|
||||
this.config = ruleConfig;
|
||||
@ -196,24 +198,24 @@ define([
|
||||
|
||||
Object.keys(this.textInputs).forEach(function (inputKey) {
|
||||
self.textInputs[inputKey].prop('value', self.config[inputKey] || '');
|
||||
self.textInputs[inputKey].on('input', function () {
|
||||
self.listenTo(self.textInputs[inputKey], 'input', function () {
|
||||
onTextInput(this, inputKey);
|
||||
});
|
||||
});
|
||||
|
||||
this.deleteButton.on('click', this.remove);
|
||||
this.duplicateButton.on('click', this.duplicate);
|
||||
this.addConditionButton.on('click', function () {
|
||||
this.listenTo(this.deleteButton, 'click', this.remove);
|
||||
this.listenTo(this.duplicateButton, 'click', this.duplicate);
|
||||
this.listenTo(this.addConditionButton, 'click', function () {
|
||||
self.initCondition();
|
||||
});
|
||||
this.toggleConfigButton.on('click', toggleConfig);
|
||||
this.trigger.on('change', onTriggerInput);
|
||||
this.listenTo(this.toggleConfigButton, 'click', toggleConfig);
|
||||
this.listenTo(this.trigger, 'change', onTriggerInput);
|
||||
|
||||
this.title.html(self.config.name);
|
||||
this.description.html(self.config.description);
|
||||
this.trigger.prop('value', self.config.trigger);
|
||||
|
||||
this.grippy.on('mousedown', onDragStart);
|
||||
this.listenTo(this.grippy, 'mousedown', onDragStart);
|
||||
this.widgetDnD.on('drop', function () {
|
||||
this.domElement.show();
|
||||
$('.t-drag-indicator').hide();
|
||||
@ -258,6 +260,10 @@ define([
|
||||
palette.destroy();
|
||||
});
|
||||
this.iconInput.destroy();
|
||||
this.stopListening();
|
||||
this.conditions.forEach(function (condition) {
|
||||
condition.destroy();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,7 @@ define([
|
||||
'./ConditionManager',
|
||||
'./TestDataManager',
|
||||
'./WidgetDnD',
|
||||
'./eventHelpers',
|
||||
'lodash',
|
||||
'zepto'
|
||||
], function (
|
||||
@ -12,6 +13,7 @@ define([
|
||||
ConditionManager,
|
||||
TestDataManager,
|
||||
WidgetDnD,
|
||||
eventHelpers,
|
||||
_,
|
||||
$
|
||||
) {
|
||||
@ -32,6 +34,8 @@ define([
|
||||
* @param {MCT} openmct An MCT instance
|
||||
*/
|
||||
function SummaryWidget(domainObject, openmct) {
|
||||
eventHelpers.extend(this);
|
||||
|
||||
this.domainObject = domainObject;
|
||||
this.openmct = openmct;
|
||||
|
||||
@ -86,7 +90,7 @@ define([
|
||||
self.outerWrapper.toggleClass('expanded-widget-test-data');
|
||||
self.toggleTestDataControl.toggleClass('expanded');
|
||||
}
|
||||
this.toggleTestDataControl.on('click', toggleTestData);
|
||||
this.listenTo(this.toggleTestDataControl, 'click', toggleTestData);
|
||||
|
||||
/**
|
||||
* Toggles the configuration area for rules in the view
|
||||
@ -96,7 +100,7 @@ define([
|
||||
self.outerWrapper.toggleClass('expanded-widget-rules');
|
||||
self.toggleRulesControl.toggleClass('expanded');
|
||||
}
|
||||
this.toggleRulesControl.on('click', toggleRules);
|
||||
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
|
||||
|
||||
openmct.$injector.get('objectService')
|
||||
.getObjects([id])
|
||||
@ -160,13 +164,15 @@ define([
|
||||
this.widgetDnD = new WidgetDnD(this.domElement, this.domainObject.configuration.ruleOrder, this.rulesById);
|
||||
this.initRule('default', 'Default');
|
||||
this.domainObject.configuration.ruleOrder.forEach(function (ruleId) {
|
||||
self.initRule(ruleId);
|
||||
if (ruleId !== 'default') {
|
||||
self.initRule(ruleId);
|
||||
}
|
||||
});
|
||||
this.refreshRules();
|
||||
this.updateWidget();
|
||||
this.updateView();
|
||||
|
||||
this.addRuleButton.on('click', this.addRule);
|
||||
this.listenTo(this.addRuleButton, 'click', this.addRule);
|
||||
this.conditionManager.on('receiveTelemetry', this.executeRules, this);
|
||||
this.widgetDnD.on('drop', this.reorder, this);
|
||||
};
|
||||
@ -178,11 +184,14 @@ define([
|
||||
SummaryWidget.prototype.destroy = function (container) {
|
||||
this.editListenerUnsubscribe();
|
||||
this.conditionManager.destroy();
|
||||
this.testDataManager.destroy();
|
||||
this.widgetDnD.destroy();
|
||||
this.watchForChangesUnsubscribe();
|
||||
Object.values(this.rulesById).forEach(function (rule) {
|
||||
rule.destroy();
|
||||
});
|
||||
|
||||
this.stopListening();
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2,12 +2,14 @@ define([
|
||||
'text!../res/testDataItemTemplate.html',
|
||||
'./input/ObjectSelect',
|
||||
'./input/KeySelect',
|
||||
'./eventHelpers',
|
||||
'EventEmitter',
|
||||
'zepto'
|
||||
], function (
|
||||
itemTemplate,
|
||||
ObjectSelect,
|
||||
KeySelect,
|
||||
eventHelpers,
|
||||
EventEmitter,
|
||||
$
|
||||
) {
|
||||
@ -24,6 +26,7 @@ define([
|
||||
* @constructor
|
||||
*/
|
||||
function TestDataItem(itemConfig, index, conditionManager) {
|
||||
eventHelpers.extend(this);
|
||||
this.config = itemConfig;
|
||||
this.index = index;
|
||||
this.conditionManager = conditionManager;
|
||||
@ -70,16 +73,17 @@ define([
|
||||
function onValueInput(event) {
|
||||
var elem = event.target,
|
||||
value = (isNaN(elem.valueAsNumber) ? elem.value : elem.valueAsNumber);
|
||||
|
||||
self.eventEmitter.emit('change', {
|
||||
value: value,
|
||||
property: 'value',
|
||||
index: self.index
|
||||
});
|
||||
if (elem.tagName.toUpperCase() === 'INPUT') {
|
||||
self.eventEmitter.emit('change', {
|
||||
value: value,
|
||||
property: 'value',
|
||||
index: self.index
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.deleteButton.on('click', this.remove);
|
||||
this.duplicateButton.on('click', this.duplicate);
|
||||
this.listenTo(this.deleteButton, 'click', this.remove);
|
||||
this.listenTo(this.duplicateButton, 'click', this.duplicate);
|
||||
|
||||
this.selects.object = new ObjectSelect(this.config, this.conditionManager);
|
||||
this.selects.key = new KeySelect(
|
||||
@ -97,8 +101,7 @@ define([
|
||||
Object.values(this.selects).forEach(function (select) {
|
||||
$('.t-configuration', self.domElement).append(select.getDOM());
|
||||
});
|
||||
|
||||
$(this.domElement).on('input', 'input', onValueInput);
|
||||
this.listenTo(this.domElement, 'input', onValueInput);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,6 +140,11 @@ define([
|
||||
TestDataItem.prototype.remove = function () {
|
||||
var self = this;
|
||||
this.eventEmitter.emit('remove', self.index);
|
||||
this.stopListening();
|
||||
|
||||
Object.values(this.selects).forEach(function (select) {
|
||||
select.destroy();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,11 @@
|
||||
define([
|
||||
'./eventHelpers',
|
||||
'text!../res/testDataTemplate.html',
|
||||
'./TestDataItem',
|
||||
'zepto',
|
||||
'lodash'
|
||||
], function (
|
||||
eventHelpers,
|
||||
testDataTemplate,
|
||||
TestDataItem,
|
||||
$,
|
||||
@ -18,6 +20,7 @@ define([
|
||||
* @param {MCT} openmct and MCT instance
|
||||
*/
|
||||
function TestDataManager(domainObject, conditionManager, openmct) {
|
||||
eventHelpers.extend(this);
|
||||
var self = this;
|
||||
|
||||
this.domainObject = domainObject;
|
||||
@ -45,10 +48,10 @@ define([
|
||||
self.updateTestCache();
|
||||
}
|
||||
|
||||
this.addItemButton.on('click', function () {
|
||||
this.listenTo(this.addItemButton, 'click', function () {
|
||||
self.initItem();
|
||||
});
|
||||
this.testDataInput.on('change', toggleTestData);
|
||||
this.listenTo(this.testDataInput, 'change', toggleTestData);
|
||||
|
||||
this.evaluator.setTestDataCache(this.testCache);
|
||||
this.evaluator.useTestData(false);
|
||||
@ -186,5 +189,12 @@ define([
|
||||
this.openmct.objects.mutate(this.domainObject, 'configuration.testDataConfig', this.config);
|
||||
};
|
||||
|
||||
TestDataManager.prototype.destroy = function () {
|
||||
this.items.forEach(function (item) {
|
||||
item.remove();
|
||||
});
|
||||
this.stopListening();
|
||||
};
|
||||
|
||||
return TestDataManager;
|
||||
});
|
||||
|
74
src/plugins/summaryWidget/src/eventHelpers.js
Normal file
74
src/plugins/summaryWidget/src/eventHelpers.js
Normal file
@ -0,0 +1,74 @@
|
||||
var listenersCount = 0;
|
||||
/*global define*/
|
||||
// jscs:disable disallowDanglingUnderscores
|
||||
define([], function () {
|
||||
var helperFunctions = {
|
||||
listenTo: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
var listener = {
|
||||
object: object,
|
||||
event: event,
|
||||
callback: callback,
|
||||
context: context,
|
||||
_cb: !!context ? callback.bind(context) : callback
|
||||
};
|
||||
if (object.$watch && event.indexOf('change:') === 0) {
|
||||
var scopePath = event.replace('change:', '');
|
||||
listener.unlisten = object.$watch(scopePath, listener._cb, true);
|
||||
} else if (object.$on) {
|
||||
listener.unlisten = object.$on(event, listener._cb);
|
||||
} else if (object.addEventListener) {
|
||||
object.addEventListener(event, listener._cb);
|
||||
} else {
|
||||
object.on(event, listener._cb);
|
||||
}
|
||||
this._listeningTo.push(listener);
|
||||
listenersCount++;
|
||||
},
|
||||
|
||||
stopListening: function (object, event, callback, context) {
|
||||
if (!this._listeningTo) {
|
||||
this._listeningTo = [];
|
||||
}
|
||||
|
||||
this._listeningTo.filter(function (listener) {
|
||||
if (object && object !== listener.object) {
|
||||
return false;
|
||||
}
|
||||
if (event && event !== listener.event) {
|
||||
return false;
|
||||
}
|
||||
if (callback && callback !== listener.callback) {
|
||||
return false;
|
||||
}
|
||||
if (context && context !== listener.context) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(function (listener) {
|
||||
if (listener.unlisten) {
|
||||
listener.unlisten();
|
||||
} else if (listener.object.removeEventListener) {
|
||||
listener.object.removeEventListener(listener.event, listener._cb);
|
||||
} else {
|
||||
listener.object.off(listener.event, listener._cb);
|
||||
}
|
||||
listenersCount--;
|
||||
return listener;
|
||||
})
|
||||
.forEach(function (listener) {
|
||||
this._listeningTo.splice(this._listeningTo.indexOf(listener), 1);
|
||||
}, this);
|
||||
},
|
||||
|
||||
extend: function (object) {
|
||||
object.listenTo = helperFunctions.listenTo;
|
||||
object.stopListening = helperFunctions.stopListening;
|
||||
}
|
||||
};
|
||||
|
||||
return helperFunctions;
|
||||
});
|
@ -1,4 +1,8 @@
|
||||
define(['./Select'], function (Select) {
|
||||
define([
|
||||
'./Select'
|
||||
], function (
|
||||
Select
|
||||
) {
|
||||
|
||||
/**
|
||||
* Create a {Select} element whose composition is dynamically updated with
|
||||
@ -62,7 +66,7 @@ define(['./Select'], function (Select) {
|
||||
onMetadataLoad();
|
||||
}
|
||||
|
||||
this.objectSelect.on('change', onObjectChange);
|
||||
this.objectSelect.on('change', onObjectChange, this);
|
||||
this.manager.on('metadata', onMetadataLoad);
|
||||
|
||||
return this.select;
|
||||
@ -85,6 +89,10 @@ define(['./Select'], function (Select) {
|
||||
}
|
||||
};
|
||||
|
||||
KeySelect.prototype.destroy = function () {
|
||||
this.objectSelect.destroy();
|
||||
};
|
||||
|
||||
return KeySelect;
|
||||
|
||||
});
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(['./Select'], function (Select) {
|
||||
define([
|
||||
'./Select',
|
||||
'../eventHelpers'
|
||||
], function (
|
||||
Select,
|
||||
eventHelpers
|
||||
) {
|
||||
|
||||
/**
|
||||
* Create a {Select} element whose composition is dynamically updated with
|
||||
@ -17,6 +23,7 @@ define(['./Select'], function (Select) {
|
||||
var NULLVALUE = '- Select Comparison -';
|
||||
|
||||
function OperationSelect(config, keySelect, manager, changeCallback) {
|
||||
eventHelpers.extend(this);
|
||||
var self = this;
|
||||
|
||||
this.config = config;
|
||||
@ -31,7 +38,7 @@ define(['./Select'], function (Select) {
|
||||
this.select.hide();
|
||||
this.select.addOption('', NULLVALUE);
|
||||
if (changeCallback) {
|
||||
this.select.on('change', changeCallback);
|
||||
this.listenTo(this.select, 'change', changeCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,7 +70,6 @@ define(['./Select'], function (Select) {
|
||||
}
|
||||
self.select.setSelected(self.config.operation);
|
||||
}
|
||||
|
||||
this.keySelect.on('change', onKeyChange);
|
||||
this.manager.on('metadata', onMetadataLoad);
|
||||
|
||||
@ -109,6 +115,10 @@ define(['./Select'], function (Select) {
|
||||
});
|
||||
};
|
||||
|
||||
OperationSelect.prototype.destroy = function () {
|
||||
this.stopListening();
|
||||
};
|
||||
|
||||
return OperationSelect;
|
||||
|
||||
});
|
||||
|
@ -1,13 +1,14 @@
|
||||
define([
|
||||
'../eventHelpers',
|
||||
'text!../../res/input/paletteTemplate.html',
|
||||
'EventEmitter',
|
||||
'zepto'
|
||||
], function (
|
||||
eventHelpers,
|
||||
paletteTemplate,
|
||||
EventEmitter,
|
||||
$
|
||||
) {
|
||||
|
||||
/**
|
||||
* Instantiates a new Open MCT Color Palette input
|
||||
* @constructor
|
||||
@ -19,6 +20,8 @@ define([
|
||||
* up to the descendent class
|
||||
*/
|
||||
function Palette(cssClass, container, items) {
|
||||
eventHelpers.extend(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
this.cssClass = cssClass;
|
||||
@ -49,8 +52,8 @@ define([
|
||||
|
||||
$('.menu', self.domElement).hide();
|
||||
|
||||
$(document).on('click', this.hideMenu);
|
||||
$('.l-click-area', self.domElement).on('click', function (event) {
|
||||
this.listenTo($(document), 'click', this.hideMenu);
|
||||
this.listenTo($('.l-click-area', self.domElement), 'click', function (event) {
|
||||
event.stopPropagation();
|
||||
$('.menu', self.container).hide();
|
||||
$('.menu', self.domElement).show();
|
||||
@ -69,7 +72,7 @@ define([
|
||||
$('.menu', self.domElement).hide();
|
||||
}
|
||||
|
||||
$('.s-palette-item', self.domElement).on('click', handleItemClick);
|
||||
this.listenTo($('.s-palette-item', self.domElement), 'click', handleItemClick);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +86,7 @@ define([
|
||||
* Clean up any event listeners registered to DOM elements external to the widget
|
||||
*/
|
||||
Palette.prototype.destroy = function () {
|
||||
$(document).off('click', this.hideMenu);
|
||||
this.stopListening();
|
||||
};
|
||||
|
||||
Palette.prototype.hideMenu = function () {
|
||||
|
@ -1,8 +1,10 @@
|
||||
define([
|
||||
'../eventHelpers',
|
||||
'text!../../res/input/selectTemplate.html',
|
||||
'EventEmitter',
|
||||
'zepto'
|
||||
], function (
|
||||
eventHelpers,
|
||||
selectTemplate,
|
||||
EventEmitter,
|
||||
$
|
||||
@ -14,6 +16,8 @@ define([
|
||||
* @constructor
|
||||
*/
|
||||
function Select() {
|
||||
eventHelpers.extend(this);
|
||||
|
||||
var self = this;
|
||||
|
||||
this.domElement = $(selectTemplate);
|
||||
@ -36,7 +40,7 @@ define([
|
||||
self.eventEmitter.emit('change', value[0]);
|
||||
}
|
||||
|
||||
$('select', this.domElement).on('change', onChange);
|
||||
this.listenTo($('select', this.domElement), 'change', onChange, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -140,5 +144,9 @@ define([
|
||||
$('.equal-to').removeClass('hidden');
|
||||
};
|
||||
|
||||
Select.prototype.destroy = function () {
|
||||
this.stopListening();
|
||||
};
|
||||
|
||||
return Select;
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user