+
0) {
this.$scope.totalWidth = tableWidth + 'px';
@@ -439,7 +434,6 @@ define(
prevLargest[key] = JSON.parse(JSON.stringify(row[key]));
}
}
-
});
return prevLargest;
}, JSON.parse(JSON.stringify(rows[0] || {})));
@@ -447,26 +441,30 @@ define(
};
/**
- * Calculates the widest row in the table, pads that row, and adds
- * it to the table. Allows the table to size itself, then uses this
- * as basis for column dimensions.
+ * Calculates the widest row in the table, and if necessary, resizes
+ * the table accordingly
+ *
+ * @param rows the rows on which to resize
+ * @returns {Promise} a promise that will resolve when resizing has
+ * occurred.
* @private
*/
- MCTTableController.prototype.resize = function (){
- var self = this;
+ MCTTableController.prototype.resize = function (rows){
+ //Calculate largest row
+ var largestRow = this.buildLargestRow(rows);
- this.$scope.sizingRow = this.buildLargestRow(this.$scope.displayRows);
-
- //Wait a timeout to allow digest of previous change to visible
- // rows to happen.
- this.$timeout(function () {
- self.$scope.visibleRows = [];
- self.setElementSizes();
- });
+ // Has it changed? If so, set the the 'sizing' row which
+ // determines column widths
+ if (JSON.stringify(largestRow) !== JSON.stringify(this.$scope.sizingRow)){
+ this.$scope.sizingRow = largestRow;
+ return this.$timeout(this.setElementSizes.bind(this));
+ } else {
+ return fastPromise(undefined);
+ }
};
/**
- * @priate
+ * @private
*/
MCTTableController.prototype.filterAndSort = function (rows) {
var displayRows = rows;
@@ -484,17 +482,14 @@ define(
* Update rows with new data. If filtering is enabled, rows
* will be sorted before display.
*/
- MCTTableController.prototype.updateRows = function (newRows) {
- //Reset visible rows because new row data available.
- this.$scope.visibleRows = [];
-
+ MCTTableController.prototype.setRows = function (newRows) {
//Nothing to show because no columns visible
- if (!this.$scope.displayHeaders) {
+ if (!this.$scope.displayHeaders || !newRows) {
return;
}
this.filterAndSort(newRows || []);
- this.resize();
+ this.resize(newRows).then(this.setVisibleRows.bind(this));
};
/**
diff --git a/platform/features/table/src/controllers/RTTelemetryTableController.js b/platform/features/table/src/controllers/RealtimeTableController.js
similarity index 58%
rename from platform/features/table/src/controllers/RTTelemetryTableController.js
rename to platform/features/table/src/controllers/RealtimeTableController.js
index 8a61d61b5e..cf58cb236e 100644
--- a/platform/features/table/src/controllers/RTTelemetryTableController.js
+++ b/platform/features/table/src/controllers/RealtimeTableController.js
@@ -37,7 +37,7 @@ define(
* @param telemetryFormatter
* @constructor
*/
- function RTTelemetryTableController($scope, telemetryHandler, telemetryFormatter) {
+ function RealtimeTableController($scope, telemetryHandler, telemetryFormatter) {
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
$scope.autoScroll = false;
@@ -66,58 +66,31 @@ define(
});
}
- RTTelemetryTableController.prototype = Object.create(TableController.prototype);
+ RealtimeTableController.prototype = Object.create(TableController.prototype);
- /**
- Override the subscribe function defined on the parent controller in
- order to handle realtime telemetry instead of historical.
- */
- RTTelemetryTableController.prototype.subscribe = function () {
- var self = this;
- self.$scope.rows = undefined;
- (this.subscriptions || []).forEach(function (unsubscribe){
- unsubscribe();
- });
+ RealtimeTableController.prototype.addRealtimeData = function() {
+ var self = this,
+ datum,
+ row;
+ this.handle.getTelemetryObjects().forEach(function (telemetryObject){
+ datum = self.handle.getDatum(telemetryObject);
+ if (datum) {
+ //Populate row values from telemetry datum
+ row = self.table.getRowValues(telemetryObject, datum);
+ self.$scope.rows.push(row);
- if (this.handle) {
- this.handle.unsubscribe();
- }
-
- function updateData(){
- var datum,
- row;
- self.handle.getTelemetryObjects().forEach(function (telemetryObject){
- datum = self.handle.getDatum(telemetryObject);
- if (datum) {
- row = self.table.getRowValues(telemetryObject, datum);
- if (!self.$scope.rows){
- self.$scope.rows = [row];
- self.$scope.$digest();
- } else {
- self.$scope.rows.push(row);
-
- if (self.$scope.rows.length > self.maxRows) {
- self.$scope.$broadcast('remove:row', 0);
- self.$scope.rows.shift();
- }
-
- self.$scope.$broadcast('add:row',
- self.$scope.rows.length - 1);
- }
+ //Inform table that a new row has been added
+ if (self.$scope.rows.length > self.maxRows) {
+ self.$scope.$broadcast('remove:row', 0);
+ self.$scope.rows.shift();
}
- });
- }
+ self.$scope.$broadcast('add:row',
+ self.$scope.rows.length - 1);
+ }
+ });
+ }
- this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
- this.$scope.domainObject,
- updateData,
- true // Lossless
- );
-
- this.setup();
- };
-
- return RTTelemetryTableController;
+ return RealtimeTableController;
}
);
diff --git a/platform/features/table/src/controllers/TableOptionsController.js b/platform/features/table/src/controllers/TableOptionsController.js
index c3b479073c..499e91efbc 100644
--- a/platform/features/table/src/controllers/TableOptionsController.js
+++ b/platform/features/table/src/controllers/TableOptionsController.js
@@ -51,13 +51,22 @@ define(
this.$scope = $scope;
this.domainObject = $scope.domainObject;
+ this.listeners = [];
$scope.columnsForm = {};
- this.domainObject.getCapability('mutation').listen(function (model) {
- self.populateForm(model);
+ $scope.$watch('domainObject', function(domainObject) {
+ self.populateForm(domainObject.getModel());
+
+ self.listeners.push(self.domainObject.getCapability('mutation').listen(function (model) {
+ self.populateForm(model);
+ }));
});
+ /**
+ * Maintain a configuration object on scope that stores column
+ * configuration. On change, synchronize with object model.
+ */
$scope.$watchCollection('configuration.table.columns', function (columns){
if (columns){
self.domainObject.useCapability('mutation', function (model) {
@@ -67,6 +76,15 @@ define(
}
});
+ /**
+ * Destroy all mutation listeners
+ */
+ $scope.$on('$destroy', function () {
+ self.listeners.forEach(function (listener) {
+ listener();
+ });
+ })
+
}
TableOptionsController.prototype.populateForm = function (model) {
@@ -86,7 +104,7 @@ define(
'key': key
});
});
- this.$scope.configuration = JSON.parse(JSON.stringify(model.configuration));
+ this.$scope.configuration = JSON.parse(JSON.stringify(model.configuration || {}));
};
return TableOptionsController;
diff --git a/platform/features/table/src/controllers/TelemetryTableController.js b/platform/features/table/src/controllers/TelemetryTableController.js
index e579c5eeb8..b41cb940f5 100644
--- a/platform/features/table/src/controllers/TelemetryTableController.js
+++ b/platform/features/table/src/controllers/TelemetryTableController.js
@@ -52,19 +52,15 @@ define(
this.$scope = $scope;
this.columns = {}; //Range and Domain columns
this.handle = undefined;
- //this.pending = false;
this.telemetryHandler = telemetryHandler;
this.table = new TableConfiguration($scope.domainObject,
telemetryFormatter);
this.changeListeners = [];
- $scope.rows = undefined;
+ $scope.rows = [];
// Subscribe to telemetry when a domain object becomes available
- this.$scope.$watch('domainObject', function(domainObject){
- if (!domainObject)
- return;
-
+ this.$scope.$watch('domainObject', function(){
self.subscribe();
self.registerChangeListeners();
});
@@ -73,16 +69,24 @@ define(
this.$scope.$on("$destroy", this.destroy.bind(this));
}
+ /**
+ * @private
+ */
+ TelemetryTableController.prototype.unregisterChangeListeners = function () {
+ this.changeListeners.forEach(function (listener) {
+ return listener && listener();
+ });
+ this.changeListeners = [];
+ }
+
/**
* Defer registration of change listeners until domain object is
* available in order to avoid race conditions
* @private
*/
TelemetryTableController.prototype.registerChangeListeners = function () {
- this.changeListeners.forEach(function (listener) {
- return listener && listener();
- });
- this.changeListeners = [];
+ this.unregisterChangeListeners();
+
// When composition changes, re-subscribe to the various
// telemetry subscriptions
this.changeListeners.push(this.$scope.$watchCollection(
@@ -103,25 +107,37 @@ define(
}
};
+ /**
+ * Function for handling realtime data when it is available. This
+ * will be called by the telemetry framework when new data is
+ * available.
+ *
+ * Method should be overridden by specializing class.
+ */
+ TelemetryTableController.prototype.addRealtimeData = function () {
+ };
+
+ /**
+ * Function for handling historical data. Will be called by
+ * telemetry framework when requested historical data is available.
+ * Should be overridden by specializing class.
+ */
+ TelemetryTableController.prototype.addHistoricalData = function () {
+ };
+
/**
Create a new subscription. This can be overridden by children to
change default behaviour (which is to retrieve historical telemetry
only).
*/
TelemetryTableController.prototype.subscribe = function () {
- var self = this;
-
if (this.handle) {
this.handle.unsubscribe();
}
- //Noop because not supporting realtime data right now
- function noop(){
- }
-
this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
this.$scope.domainObject,
- noop,
+ this.addRealtimeData.bind(this),
true // Lossless
);
@@ -130,28 +146,6 @@ define(
this.setup();
};
- /**
- * Populates historical data on scope when it becomes available
- * @private
- */
- TelemetryTableController.prototype.addHistoricalData = function () {
- var rowData = [],
- self = this;
-
- this.handle.getTelemetryObjects().forEach(function (telemetryObject){
- var series = self.handle.getSeries(telemetryObject) || {},
- pointCount = series.getPointCount ? series.getPointCount() : 0,
- i = 0;
-
- for (; i < pointCount; i++) {
- rowData.push(self.table.getRowValues(telemetryObject,
- self.handle.makeDatum(telemetryObject, series, i)));
- }
- });
-
- this.$scope.rows = rowData;
- };
-
/**
* Setup table columns based on domain object metadata
*/
@@ -162,7 +156,9 @@ define(
if (handle) {
handle.promiseTelemetryObjects().then(function () {
- table.buildColumns(handle.getMetadata());
+ self.$scope.headers = []
+ self.$scope.rows = [];
+ table.populateColumns(handle.getMetadata());
self.filterColumns();
@@ -176,26 +172,14 @@ define(
}
};
- /**
- * @private
- * @param object The object for which data is available (table may
- * be composed of multiple objects)
- * @param datum The data received from the telemetry source
- */
- TelemetryTableController.prototype.updateRows = function (object, datum) {
- this.$scope.rows.push(this.table.getRowValues(object, datum));
- };
-
/**
* When column configuration changes, update the visible headers
* accordingly.
* @private
*/
- TelemetryTableController.prototype.filterColumns = function (columnConfig) {
- if (!columnConfig){
- columnConfig = this.table.getColumnConfiguration();
- this.table.saveColumnConfiguration(columnConfig);
- }
+ TelemetryTableController.prototype.filterColumns = function () {
+ var columnConfig = this.table.buildColumnConfiguration();
+
//Populate headers with visible columns (determined by configuration)
this.$scope.headers = Object.keys(columnConfig).filter(function (column) {
return columnConfig[column];
diff --git a/platform/features/table/test/TableConfigurationSpec.js b/platform/features/table/test/TableConfigurationSpec.js
index 86a18aee5a..79583d09f8 100644
--- a/platform/features/table/test/TableConfigurationSpec.js
+++ b/platform/features/table/test/TableConfigurationSpec.js
@@ -116,10 +116,10 @@ define(
}];
beforeEach(function() {
- table.buildColumns(metadata);
+ table.populateColumns(metadata);
});
- it("populates the columns attribute", function() {
+ it("populates columns", function() {
expect(table.columns.length).toBe(5);
});
@@ -141,7 +141,7 @@ define(
it("Provides a default configuration with all columns" +
" visible", function() {
- var configuration = table.getColumnConfiguration();
+ var configuration = table.buildColumnConfiguration();
expect(configuration).toBeDefined();
expect(Object.keys(configuration).every(function(key){
@@ -160,7 +160,7 @@ define(
};
mockModel.configuration = modelConfig;
- tableConfig = table.getColumnConfiguration();
+ tableConfig = table.buildColumnConfiguration();
expect(tableConfig).toBeDefined();
expect(tableConfig['Range 1']).toBe(false);
@@ -191,6 +191,9 @@ define(
expect(mockTelemetryFormatter.formatRangeValue).toHaveBeenCalled();
});
});
+ /**
+ * TODO: Add test for saving column config
+ */
});
});
}
diff --git a/platform/features/table/test/controllers/TelemetryTableControllerSpec.js b/platform/features/table/test/controllers/HistoricalTableControllerSpec.js
similarity index 94%
rename from platform/features/table/test/controllers/TelemetryTableControllerSpec.js
rename to platform/features/table/test/controllers/HistoricalTableControllerSpec.js
index 03f62f11e3..f000529467 100644
--- a/platform/features/table/test/controllers/TelemetryTableControllerSpec.js
+++ b/platform/features/table/test/controllers/HistoricalTableControllerSpec.js
@@ -23,7 +23,7 @@
define(
[
- "../../src/controllers/TelemetryTableController"
+ "../../src/controllers/HistoricalTableController"
],
function (TableController) {
"use strict";
@@ -73,14 +73,14 @@ define(
mockTable = jasmine.createSpyObj('table',
[
- 'buildColumns',
- 'getColumnConfiguration',
+ 'populateColumns',
+ 'buildColumnConfiguration',
'getRowValues',
'saveColumnConfiguration'
]
);
mockTable.columns = [];
- mockTable.getColumnConfiguration.andReturn(mockConfiguration);
+ mockTable.buildColumnConfiguration.andReturn(mockConfiguration);
mockDomainObject= jasmine.createSpyObj('domainObject', [
'getCapability',
@@ -126,21 +126,18 @@ define(
expect(mockTelemetryHandle.unsubscribe).toHaveBeenCalled();
});
- describe('the controller makes use of the table', function () {
+ describe('makes use of the table', function () {
it('to create column definitions from telemetry' +
' metadata', function () {
controller.setup();
- expect(mockTable.buildColumns).toHaveBeenCalled();
+ expect(mockTable.populateColumns).toHaveBeenCalled();
});
it('to create column configuration, which is written to the' +
' object model', function () {
- var mockModel = {};
-
controller.setup();
- expect(mockTable.getColumnConfiguration).toHaveBeenCalled();
- expect(mockTable.saveColumnConfiguration).toHaveBeenCalled();
+ expect(mockTable.buildColumnConfiguration).toHaveBeenCalled();
});
});
diff --git a/platform/features/table/test/controllers/MCTTableControllerSpec.js b/platform/features/table/test/controllers/MCTTableControllerSpec.js
index 5ee9f21c41..7bca5c65b4 100644
--- a/platform/features/table/test/controllers/MCTTableControllerSpec.js
+++ b/platform/features/table/test/controllers/MCTTableControllerSpec.js
@@ -118,7 +118,7 @@ define(
});
it('Sets rows on scope when rows change', function() {
- controller.updateRows(testRows);
+ controller.setRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
expect(mockScope.displayRows).toEqual(testRows);
});
@@ -130,7 +130,7 @@ define(
'col2': {'text': 'ghi'},
'col3': {'text': 'row3 col3'}
};
- controller.updateRows(testRows);
+ controller.setRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
testRows.push(row4);
addRowFunc(undefined, 3);
@@ -139,7 +139,7 @@ define(
it('Supports removing rows individually', function() {
var removeRowFunc = mockScope.$on.calls[mockScope.$on.calls.length-1].args[1];
- controller.updateRows(testRows);
+ controller.setRows(testRows);
expect(mockScope.displayRows.length).toBe(3);
removeRowFunc(undefined, 2);
expect(mockScope.displayRows.length).toBe(2);
@@ -211,20 +211,20 @@ define(
mockScope.displayRows = controller.sortRows(testRows.slice(0));
mockScope.rows.push(row4);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows[0].col2.text).toEqual('xyz');
mockScope.rows.push(row5);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows[4].col2.text).toEqual('aaa');
mockScope.rows.push(row6);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows[2].col2.text).toEqual('ggg');
//Add a duplicate row
mockScope.rows.push(row6);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows[2].col2.text).toEqual('ggg');
expect(mockScope.displayRows[3].col2.text).toEqual('ggg');
});
@@ -240,12 +240,12 @@ define(
mockScope.displayRows = controller.filterRows(testRows);
mockScope.rows.push(row5);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows.length).toBe(2);
expect(mockScope.displayRows[1].col2.text).toEqual('aaa');
mockScope.rows.push(row6);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows.length).toBe(2);
//Row was not added because does not match filter
});
@@ -259,11 +259,11 @@ define(
mockScope.displayRows = testRows.slice(0);
mockScope.rows.push(row5);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows[3].col2.text).toEqual('aaa');
mockScope.rows.push(row6);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(mockScope.displayRows[4].col2.text).toEqual('ggg');
});
@@ -282,7 +282,7 @@ define(
mockScope.displayRows = testRows.slice(0);
mockScope.rows.push(row7);
- controller.newRow(undefined, mockScope.rows.length-1);
+ controller.addRow(undefined, mockScope.rows.length-1);
expect(controller.$scope.sizingRow.col2).toEqual({text: 'some longer string'});
});
diff --git a/platform/features/table/test/controllers/RTTelemetryTableControllerSpec.js b/platform/features/table/test/controllers/RealtimeTableControllerSpec.js
similarity index 94%
rename from platform/features/table/test/controllers/RTTelemetryTableControllerSpec.js
rename to platform/features/table/test/controllers/RealtimeTableControllerSpec.js
index 59911d1771..e0b6978d88 100644
--- a/platform/features/table/test/controllers/RTTelemetryTableControllerSpec.js
+++ b/platform/features/table/test/controllers/RealtimeTableControllerSpec.js
@@ -23,7 +23,7 @@
define(
[
- "../../src/controllers/RTTelemetryTableController"
+ "../../src/controllers/RealtimeTableController"
],
function (TableController) {
"use strict";
@@ -77,14 +77,14 @@ define(
mockTable = jasmine.createSpyObj('table',
[
- 'buildColumns',
- 'getColumnConfiguration',
+ 'populateColumns',
+ 'buildColumnConfiguration',
'getRowValues',
'saveColumnConfiguration'
]
);
mockTable.columns = [];
- mockTable.getColumnConfiguration.andReturn(mockConfiguration);
+ mockTable.buildColumnConfiguration.andReturn(mockConfiguration);
mockTable.getRowValues.andReturn(mockTableRow);
mockDomainObject= jasmine.createSpyObj('domainObject', [
@@ -107,13 +107,16 @@ define(
'unsubscribe',
'getDatum',
'promiseTelemetryObjects',
- 'getTelemetryObjects'
+ 'getTelemetryObjects',
+ 'request'
]);
+
// Arbitrary array with non-zero length, contents are not
// used by mocks
mockTelemetryHandle.getTelemetryObjects.andReturn([{}]);
mockTelemetryHandle.promiseTelemetryObjects.andReturn(promise(undefined));
mockTelemetryHandle.getDatum.andReturn({});
+ mockTelemetryHandle.request.andReturn(promise(undefined));
mockTelemetryHandler = jasmine.createSpyObj('telemetryHandler', [
'handle'
diff --git a/platform/features/table/test/controllers/TableOptionsControllerSpec.js b/platform/features/table/test/controllers/TableOptionsControllerSpec.js
index 9de96b5f52..483696e0e2 100644
--- a/platform/features/table/test/controllers/TableOptionsControllerSpec.js
+++ b/platform/features/table/test/controllers/TableOptionsControllerSpec.js
@@ -47,11 +47,16 @@ define(
'listen'
]);
mockDomainObject = jasmine.createSpyObj('domainObject', [
- 'getCapability'
+ 'getCapability',
+ 'getModel'
]);
mockDomainObject.getCapability.andReturn(mockCapability);
+ mockDomainObject.getModel.andReturn({});
+
mockScope = jasmine.createSpyObj('scope', [
- '$watchCollection'
+ '$watchCollection',
+ '$watch',
+ '$on'
]);
mockScope.domainObject = mockDomainObject;
@@ -59,6 +64,7 @@ define(
});
it('Registers a listener for mutation events on the object', function() {
+ mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(mockCapability.listen).toHaveBeenCalled();
});