[Code Style] Use prototypes for Events bundle

WTD-1482
This commit is contained in:
Victor Woeltjen 2015-08-11 16:01:14 -07:00
parent 7911909c5f
commit d701567b70
5 changed files with 189 additions and 188 deletions

View File

@ -35,6 +35,7 @@ define(
* *
* @memberof platform/features/events * @memberof platform/features/events
* @constructor * @constructor
* @implements {platform/features/events.EventsColumn}
* @param domainMetadata an object with the machine- and human- * @param domainMetadata an object with the machine- and human-
* readable names for this domain (in `key` and `name` * readable names for this domain (in `key` and `name`
* fields, respectively.) * fields, respectively.)
@ -42,29 +43,21 @@ define(
* formatting service, for making values human-readable. * formatting service, for making values human-readable.
*/ */
function DomainColumn(domainMetadata, telemetryFormatter) { function DomainColumn(domainMetadata, telemetryFormatter) {
return { this.domainMetadata = domainMetadata;
/** this.telemetryFormatter = telemetryFormatter;
* Get the title to display in this column's header.
* @returns {string} the title to display
* @memberof platform/features/events.DomainColumn#
*/
getTitle: function () {
return domainMetadata.name;
},
/**
* Get the text to display inside a row under this
* column.
* @returns {string} the text to display
* @memberof platform/features/events.DomainColumn#
*/
getValue: function (domainObject, data, index) {
return telemetryFormatter.formatDomainValue(
data.getDomainValue(index, domainMetadata.key)
);
}
};
} }
DomainColumn.prototype.getTitle = function () {
return this.domainMetadata.name;
};
DomainColumn.prototype.getValue = function (domainObject, data, index) {
var domainKey = this.domainMetadata.key;
return this.telemetryFormatter.formatDomainValue(
data.getDomainValue(index, domainKey)
);
};
return DomainColumn; return DomainColumn;
} }
); );

View File

@ -135,6 +135,30 @@ define(
} }
return EventListController; return EventListController;
/**
* A description of how to display a certain column of data in an
* Events view.
* @interface platform/features/events.EventColumn
* @private
*/
/**
* Get the title to display in this column's header.
* @returns {string} the title to display
* @method platform/features/events.EventColumn#getTitle
*/
/**
* Get the text to display inside a row under this
* column.
* @param {DomainObject} domainObject the domain object associated
* with this row
* @param {TelemetrySeries} series the telemetry data associated
* with this row
* @param {number} index the index of the telemetry datum associated
* with this row
* @returns {string} the text to display
* @method platform/features/events.EventColumn#getValue
*/
} }
); );

View File

@ -35,129 +35,130 @@ define(
* @param {Column[]} columns the columns to be populated * @param {Column[]} columns the columns to be populated
*/ */
function EventListPopulator(columns) { function EventListPopulator(columns) {
/* this.columns = columns;
* Look up the most recent values from a set of data objects. }
* Returns an array of objects in the order in which data
* should be displayed; each element is an object with
* two properties:
*
* * objectIndex: The index of the domain object associated
* with the data point to be displayed in that
* row.
* * pointIndex: The index of the data point itself, within
* its data set.
*
* @param {Array<Telemetry>} datas an array of the most recent
* data objects; expected to be in the same order
* as the domain objects provided at constructor
* @param {number} count the number of rows to provide
*/
function getLatestDataValues(datas, count) {
var latest = [],
candidate,
candidateTime,
used = datas.map(function () { return 0; });
// This algorithm is O(nk) for n rows and k telemetry elements; /*
// one O(k) linear search for a max is made for each of n rows. * Look up the most recent values from a set of data objects.
// This could be done in O(n lg k + k lg k), using a priority * Returns an array of objects in the order in which data
// queue (where priority is max-finding) containing k initial * should be displayed; each element is an object with
// values. For n rows, pop the max from the queue and replenish * two properties:
// the queue with a value from the data at the same *
// objectIndex, if available. * * objectIndex: The index of the domain object associated
// But k is small, so this might not give an observable * with the data point to be displayed in that
// improvement in performance. * row.
* * pointIndex: The index of the data point itself, within
* its data set.
*
* @param {Array<Telemetry>} datas an array of the most recent
* data objects; expected to be in the same order
* as the domain objects provided at constructor
* @param {number} count the number of rows to provide
*/
function getLatestDataValues(datas, count) {
var latest = [],
candidate,
candidateTime,
used = datas.map(function () { return 0; });
// Find the most recent unused data point (this will be used // This algorithm is O(nk) for n rows and k telemetry elements;
// in a loop to find and the N most recent data points) // one O(k) linear search for a max is made for each of n rows.
function findCandidate(data, i) { // This could be done in O(n lg k + k lg k), using a priority
var nextTime, // queue (where priority is max-finding) containing k initial
pointCount = data.getPointCount(), // values. For n rows, pop the max from the queue and replenish
pointIndex = pointCount - used[i] - 1; // the queue with a value from the data at the same
if (data && pointIndex >= 0) { // objectIndex, if available.
nextTime = data.getDomainValue(pointIndex); // But k is small, so this might not give an observable
if (nextTime > candidateTime) { // improvement in performance.
candidateTime = nextTime;
candidate = { // Find the most recent unused data point (this will be used
objectIndex: i, // in a loop to find and the N most recent data points)
pointIndex: pointIndex function findCandidate(data, i) {
}; var nextTime,
} pointCount = data.getPointCount(),
pointIndex = pointCount - used[i] - 1;
if (data && pointIndex >= 0) {
nextTime = data.getDomainValue(pointIndex);
if (nextTime > candidateTime) {
candidateTime = nextTime;
candidate = {
objectIndex: i,
pointIndex: pointIndex
};
} }
} }
// Assemble a list of the most recent data points
while (latest.length < count) {
// Reset variables pre-search
candidateTime = Number.NEGATIVE_INFINITY;
candidate = undefined;
// Linear search for most recent
datas.forEach(findCandidate);
if (candidate) {
// Record this data point - it is the most recent
latest.push(candidate);
// Track the data points used so we can look farther back
// in the data set on the next iteration
used[candidate.objectIndex] = used[candidate.objectIndex] + 1;
} else {
// Ran out of candidates; not enough data points
// available to fill all rows.
break;
}
}
return latest;
} }
// Assemble a list of the most recent data points
while (latest.length < count) {
// Reset variables pre-search
candidateTime = Number.NEGATIVE_INFINITY;
candidate = undefined;
return { // Linear search for most recent
/** datas.forEach(findCandidate);
* Get the text which should appear in headers for the
* provided columns.
* @memberof platform/features/events.EventListPopulator
* @returns {string[]} column headers
*/
getHeaders: function () {
return columns.map(function (column) {
return column.getTitle();
});
},
/**
* Get the contents of rows for the event list view.
* @param {TelemetrySeries[]} datas the data sets
* @param {DomainObject[]} objects the domain objects which
* provided the data sets; these should match
* index-to-index with the `datas` argument
* @param {number} count the number of rows to populate
* @memberof platform/features/events.EventListPopulator
* @returns {string[][]} an array of rows, each of which
* is an array of values which should appear
* in that row
*/
getRows: function (datas, objects, count) {
var values = getLatestDataValues(datas, count);
// Each value will become a row, which will contain if (candidate) {
// some value in each column (rendering by the // Record this data point - it is the most recent
// column object itself) latest.push(candidate);
// Additionally, we want to display the rows in reverse
// order. (i.e. from the top to the bottom of the page) // Track the data points used so we can look farther back
return values.map(function (value) { // in the data set on the next iteration
return columns.map(function (column) { used[candidate.objectIndex] = used[candidate.objectIndex] + 1;
return column.getValue( } else {
objects[value.objectIndex], // Ran out of candidates; not enough data points
datas[value.objectIndex], // available to fill all rows.
value.pointIndex break;
);
});
}).reverse();
} }
}; }
return latest;
} }
/**
* Get the text which should appear in headers for the
* provided columns.
* @memberof platform/features/events.EventListPopulator
* @returns {string[]} column headers
*/
EventListPopulator.prototype.getHeaders = function () {
return this.columns.map(function (column) {
return column.getTitle();
});
};
/**
* Get the contents of rows for the event list view.
* @param {TelemetrySeries[]} datas the data sets
* @param {DomainObject[]} objects the domain objects which
* provided the data sets; these should match
* index-to-index with the `datas` argument
* @param {number} count the number of rows to populate
* @memberof platform/features/events.EventListPopulator
* @returns {string[][]} an array of rows, each of which
* is an array of values which should appear
* in that row
*/
EventListPopulator.prototype.getRows = function (datas, objects, count) {
var values = getLatestDataValues(datas, count),
columns = this.columns;
// Each value will become a row, which will contain
// some value in each column (rendering by the
// column object itself)
// Additionally, we want to display the rows in reverse
// order. (i.e. from the top to the bottom of the page)
return values.map(function (value) {
return columns.map(function (column) {
return column.getValue(
objects[value.objectIndex],
datas[value.objectIndex],
value.pointIndex
);
});
}).reverse();
};
return EventListPopulator; return EventListPopulator;
} }

View File

@ -35,6 +35,7 @@ define(
* *
* @memberof platform/features/events * @memberof platform/features/events
* @constructor * @constructor
* @implements {platform/features/events.EventsColumn}
* @param rangeMetadata an object with the machine- and human- * @param rangeMetadata an object with the machine- and human-
* readable names for this range (in `key` and `name` * readable names for this range (in `key` and `name`
* fields, respectively.) * fields, respectively.)
@ -42,29 +43,20 @@ define(
* formatting service, for making values human-readable. * formatting service, for making values human-readable.
*/ */
function RangeColumn(rangeMetadata, telemetryFormatter) { function RangeColumn(rangeMetadata, telemetryFormatter) {
return { this.rangeMetadata = rangeMetadata;
/** this.telemetryFormatter = telemetryFormatter;
* Get the title to display in this column's header.
* @returns {string} the title to display
* @memberof platform/features/events.RangeColumn#
*/
getTitle: function () {
return rangeMetadata.name;
},
/**
* Get the text to display inside a row under this
* column.
* @returns {string} the text to display
* @memberof platform/features/events.RangeColumn#
*/
getValue: function (domainObject, data, index) {
return telemetryFormatter.formatRangeValue(
data.getRangeValue(index, rangeMetadata.key)
);
}
};
} }
RangeColumn.prototype.getTitle = function () {
return this.rangeMetadata.name;
};
RangeColumn.prototype.getValue = function (domainObject, data, index) {
var rangeKey = this.rangeMetadata.key;
return this.telemetryFormatter.formatRangeValue(
data.getRangeValue(index, rangeKey)
);
};
return RangeColumn; return RangeColumn;
} }
); );

View File

@ -33,44 +33,35 @@ define(
* Policy controlling when the Messages view should be avaliable. * Policy controlling when the Messages view should be avaliable.
* @memberof platform/features/events * @memberof platform/features/events
* @constructor * @constructor
* @implements {Policy.<View, DomainObject>}
*/ */
function MessagesViewPolicy() { function MessagesViewPolicy() {}
function hasStringTelemetry(domainObject) {
var telemetry = domainObject &&
domainObject.getCapability('telemetry'),
metadata = telemetry ? telemetry.getMetadata() : {},
ranges = metadata.ranges || [];
return ranges.some(function (range) { function hasStringTelemetry(domainObject) {
return range.format === 'string'; var telemetry = domainObject &&
}); domainObject.getCapability('telemetry'),
} metadata = telemetry ? telemetry.getMetadata() : {},
return { ranges = metadata.ranges || [];
/**
* Check whether or not a given action is allowed by this return ranges.some(function (range) {
* policy. return range.format === 'string';
* @param {Action} action the action });
* @param domainObject the domain object which will be viewed
* @returns {boolean} true if not disallowed
* @memberof platform/features/events.MessagesViewPolicy#
*/
allow: function (view, domainObject) {
// This policy only applies for the Messages view
if (view.key === 'messages') {
// The Messages view is allowed only if the domain
// object has string telemetry
if (!hasStringTelemetry(domainObject)) {
return false;
}
}
// Like all policies, allow by default.
return true;
}
};
} }
MessagesViewPolicy.prototype.allow = function (view, domainObject) {
// This policy only applies for the Messages view
if (view.key === 'messages') {
// The Messages view is allowed only if the domain
// object has string telemetry
if (!hasStringTelemetry(domainObject)) {
return false;
}
}
// Like all policies, allow by default.
return true;
};
return MessagesViewPolicy; return MessagesViewPolicy;
} }
); );