From fbf682d5fac8da8ec32231acc988c4c80fe617f7 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Sat, 20 Jun 2015 10:28:49 -0700 Subject: [PATCH 01/21] [Plot] Support fixed duration Support a fixed duration for plots, WTD-1273. --- platform/features/plot/src/PlotController.js | 7 +-- .../plot/src/elements/PlotLineBuffer.js | 6 +-- .../features/plot/src/elements/PlotUpdater.js | 52 ++++++++++++++++--- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/platform/features/plot/src/PlotController.js b/platform/features/plot/src/PlotController.js index fcce051968..106e0f94d5 100644 --- a/platform/features/plot/src/PlotController.js +++ b/platform/features/plot/src/PlotController.js @@ -99,7 +99,8 @@ define( updater = new PlotUpdater( handle, ($scope.axes[0].active || {}).key, - ($scope.axes[1].active || {}).key + ($scope.axes[1].active || {}).key, + 15000 // 15 seconds ); } @@ -161,7 +162,7 @@ define( // Unsubscribe when the plot is destroyed $scope.$on("$destroy", releaseSubscription); - + // Create a throttled update function scheduleUpdate = throttle(function () { modeOptions.getModeHandler().getSubPlots() @@ -248,4 +249,4 @@ define( return PlotController; } -); \ No newline at end of file +); diff --git a/platform/features/plot/src/elements/PlotLineBuffer.js b/platform/features/plot/src/elements/PlotLineBuffer.js index 2862a7e6be..e51e6e8a61 100644 --- a/platform/features/plot/src/elements/PlotLineBuffer.js +++ b/platform/features/plot/src/elements/PlotLineBuffer.js @@ -43,9 +43,9 @@ define( var mid = Math.floor((min + max) / 2), found = buffer[mid * 2]; - // Collisions are not wanted + // On collisions, insert at same index if (found === value) { - return -1; + return mid; } // Otherwise, if we're down to a single index, @@ -258,4 +258,4 @@ define( return PlotLineBuffer; } -); \ No newline at end of file +); diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index df7eef9abc..af19ef3eb3 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -42,8 +42,10 @@ define( * @param {TelemetryHandle} handle the handle to telemetry access * @param {string} domain the key to use when looking up domain values * @param {string} range the key to use when looking up range values + * @param {number} maxDuration maximum plot duration to display + * @param {number} maxPoints maximum number of points to display */ - function PlotUpdater(handle, domain, range, maxPoints) { + function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) { var ids = [], lines = {}, dimensions = [0, 0], @@ -107,6 +109,7 @@ define( lines = next; } + // Initialize the domain offset, based on these observed values function initializeDomainOffset(values) { domainOffset = @@ -147,6 +150,35 @@ define( [dimensionsOf(domainExtrema), 2.0 ] : [dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; origin = [originOf(domainExtrema), originOf(rangeExtrema)]; + + if (fixedDuration !== undefined) { + origin[0] = Math.min( + origin[0], + origin[0] + dimensions[0] - fixedDuration + ); + dimensions[0] = Math.max(dimensions[0], fixedDuration); + } + } + } + + // Enforce maximum duration on all plot lines; not that + // domain extrema must be up-to-date for this to behave correctly. + function enforceDuration() { + var cutoff; + + function enforceDurationForBuffer(plotLineBuffer) { + var index = plotLineBuffer.findInsertionIndex(cutoff); + if (index > 0) { + plotLineBuffer.trim(index); + } + } + + if (fixedDuration !== undefined && + domainExtrema !== undefined && + (domainExtrema[1] - domainExtrema[0] > fixedDuration)) { + cutoff = domainExtrema[1] - fixedDuration; + bufferArray.forEach(enforceDurationForBuffer); + updateExtrema(); // Extrema may have changed now } } @@ -161,6 +193,12 @@ define( } } + // Update plot extremea and enforce maximum duration + function updateBounds() { + updateExtrema(); + enforceDuration(); + } + // Handle new telemetry data function update() { var objects = handle.getTelemetryObjects(); @@ -180,8 +218,8 @@ define( // Add new data objects.forEach(addPointFor); - // Finally, update extrema - updateExtrema(); + // Then, update extrema + updateBounds(); } // Add historical data for this domain object @@ -213,12 +251,12 @@ define( line.addSeries(series, domain, range); } - // Finally, update extrema - updateExtrema(); + // Update extrema + updateBounds(); } // Use a default MAX_POINTS if none is provided - maxPoints = maxPoints || MAX_POINTS; + maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS; // Initially prepare state for these objects. // Note that this may be an empty array at this time, @@ -290,4 +328,4 @@ define( return PlotUpdater; } -); \ No newline at end of file +); From 215d3ffd728ede930c8543c52148f7928271bf4e Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Sat, 20 Jun 2015 10:39:35 -0700 Subject: [PATCH 02/21] [Plot] Tweak fixed-duration code Tweak fixed-duration code such that data is not lost; WTD-1273. --- .../features/plot/src/elements/PlotUpdater.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index af19ef3eb3..99d6916b8d 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -146,17 +146,16 @@ define( return lineBuffer.getRangeExtrema(); }).reduce(reduceExtrema); + // Calculate best-fit dimensions dimensions = (rangeExtrema[0] === rangeExtrema[1]) ? [dimensionsOf(domainExtrema), 2.0 ] : [dimensionsOf(domainExtrema), dimensionsOf(rangeExtrema)]; origin = [originOf(domainExtrema), originOf(rangeExtrema)]; + // ...then enforce a fixed duration if needed if (fixedDuration !== undefined) { - origin[0] = Math.min( - origin[0], - origin[0] + dimensions[0] - fixedDuration - ); - dimensions[0] = Math.max(dimensions[0], fixedDuration); + origin[0] = origin[0] + dimensions[0] - fixedDuration; + dimensions[0] = fixedDuration; } } } @@ -169,7 +168,9 @@ define( function enforceDurationForBuffer(plotLineBuffer) { var index = plotLineBuffer.findInsertionIndex(cutoff); if (index > 0) { - plotLineBuffer.trim(index); + // Leave one point untrimmed, such that line will + // continue off left edge of visible plot area. + plotLineBuffer.trim(index - 1); } } @@ -196,7 +197,10 @@ define( // Update plot extremea and enforce maximum duration function updateBounds() { updateExtrema(); - enforceDuration(); + // Currently not called; this will trim out off-screen + // data from the plot, but doing this will disallow things + // like pan-back, so debatable if we really want to do this + //enforceDuration(); } // Handle new telemetry data From 53f712b5061f2dc68c8f2286d866905e37d21435 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Sat, 20 Jun 2015 11:19:56 -0700 Subject: [PATCH 03/21] [Plot] Pan with alt-key While alt is held, treat drag gestures in a plot area as a pan, WTD-1273. --- platform/features/plot/src/SubPlot.js | 52 +++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/platform/features/plot/src/SubPlot.js b/platform/features/plot/src/SubPlot.js index cb0fcec077..bf2804e28e 100644 --- a/platform/features/plot/src/SubPlot.js +++ b/platform/features/plot/src/SubPlot.js @@ -56,6 +56,8 @@ define( domainOffset, mousePosition, marqueeStart, + panStart, + panStartBounds, hoverCoordinates, isHovering = false; @@ -155,6 +157,25 @@ define( tickGenerator.generateRangeTicks(RANGE_TICKS); } + function updatePan() { + var start, current, delta, nextOrigin; + + // Clear the previous panning pan-zoom state + panZoomStack.popPanZoom(); + + // Calculate what the new resulting pan-zoom should be + start = mousePositionToDomainRange(panStart); + current = mousePositionToDomainRange(mousePosition); + delta = [ current[0] - start[0], current[1] - start[1] ]; + nextOrigin = [ + panStartBounds.origin[0] - delta[0], + panStartBounds.origin[1] - delta[1] + ]; + + // ...and push a new one at the current mouse position + panZoomStack.pushPanZoom(nextOrigin, panStartBounds.dimensions); + } + // Perform a marquee zoom. function marqueeZoom(start, end) { @@ -246,14 +267,34 @@ define( if (marqueeStart) { updateMarqueeBox(); } + if (panStart) { + updatePan(); + updateDrawingBounds(); + } }, /** * Initiate a marquee zoom action. * @param $event the mouse event */ startMarquee: function ($event) { - mousePosition = marqueeStart = toMousePosition($event); - updateMarqueeBox(); + mousePosition = toMousePosition($event); + if (event.altKey) { + // Start panning + panStart = mousePosition; + panStartBounds = panZoomStack.getPanZoom(); + // We're starting a pan, so add this back as a + // state on the stack; it will get replaced + // during the pan. + panZoomStack.pushPanZoom( + panStartBounds.origin, + panStartBounds.dimensions + ); + $event.preventDefault(); + } else { + // Start marquee zooming + marqueeStart = mousePosition; + updateMarqueeBox(); + } }, /** * Complete a marquee zoom action. @@ -267,6 +308,11 @@ define( updateMarqueeBox(); updateDrawingBounds(); } + if (panStart) { + // End panning + panStart = undefined; + panStartBounds = undefined; + } }, /** * Update the drawing bounds, marquee box, and @@ -311,4 +357,4 @@ define( return SubPlot; } -); \ No newline at end of file +); From f3cbcf0abe4ec875e5e7a8d7bdd24a33ab7e717e Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Sat, 20 Jun 2015 11:21:55 -0700 Subject: [PATCH 04/21] [Plot] Update ticks during pan WTD-1273. --- platform/features/plot/src/SubPlot.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/features/plot/src/SubPlot.js b/platform/features/plot/src/SubPlot.js index bf2804e28e..c94f9ceaf8 100644 --- a/platform/features/plot/src/SubPlot.js +++ b/platform/features/plot/src/SubPlot.js @@ -270,6 +270,7 @@ define( if (panStart) { updatePan(); updateDrawingBounds(); + updateTicks(); } }, /** @@ -307,6 +308,7 @@ define( marqueeStart = undefined; updateMarqueeBox(); updateDrawingBounds(); + updateTicks(); } if (panStart) { // End panning From bc7342b127009df8198e9c724999b8e4f73f8f01 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Sat, 20 Jun 2015 11:30:20 -0700 Subject: [PATCH 05/21] [Plot] Make plot fixed duration a global default WTD-1273. --- platform/features/plot/bundle.json | 18 ++++++++++++++++-- platform/features/plot/src/PlotController.js | 10 ++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/platform/features/plot/bundle.json b/platform/features/plot/bundle.json index 2a7d6e9c73..600782c41f 100644 --- a/platform/features/plot/bundle.json +++ b/platform/features/plot/bundle.json @@ -23,8 +23,22 @@ { "key": "PlotController", "implementation": "PlotController.js", - "depends": [ "$scope", "telemetryFormatter", "telemetryHandler", "throttle" ] + "depends": [ + "$scope", + "telemetryFormatter", + "telemetryHandler", + "throttle", + "PLOT_FIXED_DURATION" + ] + } + ], + "constants": [ + { + "key": "PLOT_FIXED_DURATION", + "value": 900000, + "priority": "fallback", + "comment": "Fifteen minutes." } ] } -} \ No newline at end of file +} diff --git a/platform/features/plot/src/PlotController.js b/platform/features/plot/src/PlotController.js index 106e0f94d5..7eeca3b786 100644 --- a/platform/features/plot/src/PlotController.js +++ b/platform/features/plot/src/PlotController.js @@ -51,7 +51,13 @@ define( * * @constructor */ - function PlotController($scope, telemetryFormatter, telemetryHandler, throttle) { + function PlotController( + $scope, + telemetryFormatter, + telemetryHandler, + throttle, + PLOT_FIXED_DURATION + ) { var subPlotFactory = new SubPlotFactory(telemetryFormatter), modeOptions = new PlotModeOptions([], subPlotFactory), subplots = [], @@ -100,7 +106,7 @@ define( handle, ($scope.axes[0].active || {}).key, ($scope.axes[1].active || {}).key, - 15000 // 15 seconds + PLOT_FIXED_DURATION ); } From 8199d1d9d9d0d0e8d332b8f88b54c81fbdb773e8 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Sat, 20 Jun 2015 11:47:10 -0700 Subject: [PATCH 06/21] [Plot] Use mct-drag from plot Use mct-drag from plot such that we are able to handle mouse events which leave the plot area for marquee zoom and pan. WTD-1273. --- .../general/src/directives/MCTDrag.js | 14 +++++++--- .../features/plot/res/templates/plot.html | 5 ++-- platform/features/plot/src/SubPlot.js | 26 ++++++++++++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/platform/commonUI/general/src/directives/MCTDrag.js b/platform/commonUI/general/src/directives/MCTDrag.js index b832123f0c..f12aae5c5f 100644 --- a/platform/commonUI/general/src/directives/MCTDrag.js +++ b/platform/commonUI/general/src/directives/MCTDrag.js @@ -57,13 +57,17 @@ define( // mouse may leave this element during the drag. var body = $document.find('body'), initialPosition, + $event, delta; // Utility function to cause evaluation of mctDrag, // mctDragUp, etc function fireListener(name) { // Evaluate the expression, with current delta - scope.$eval(attrs[name], { delta: delta }); + scope.$eval(attrs[name], { + delta: delta, + $event: $event + }); // Trigger prompt digestion scope.$apply(); @@ -82,6 +86,9 @@ define( delta = currentPosition.map(function (v, i) { return v - initialPosition[i]; }); + + // Also track the plain event for firing listeners + $event = event; } // Called during a drag, on mousemove @@ -106,7 +113,7 @@ define( fireListener("mctDragUp"); - // Clear out start-of-drag position + // Clear out start-of-drag position, target initialPosition = undefined; // Don't show selection highlights, etc @@ -131,6 +138,7 @@ define( // Don't show selection highlights, etc event.preventDefault(); + return false; } @@ -148,4 +156,4 @@ define( return MCTDrag; } -); \ No newline at end of file +); diff --git a/platform/features/plot/res/templates/plot.html b/platform/features/plot/res/templates/plot.html index dd1d5356f5..fa74ef14cb 100644 --- a/platform/features/plot/res/templates/plot.html +++ b/platform/features/plot/res/templates/plot.html @@ -82,8 +82,9 @@ + mct-drag="subplot.continueDrag($event)" + mct-drag-down="subplot.startDrag($event)" + mct-drag-up="subplot.endDrag($event); plot.update()"> diff --git a/platform/features/plot/src/SubPlot.js b/platform/features/plot/src/SubPlot.js index c94f9ceaf8..2d5d2cc43d 100644 --- a/platform/features/plot/src/SubPlot.js +++ b/platform/features/plot/src/SubPlot.js @@ -58,6 +58,7 @@ define( marqueeStart, panStart, panStartBounds, + subPlotBounds, hoverCoordinates, isHovering = false; @@ -90,8 +91,7 @@ define( // pixel coordinates in the canvas area) from a mouse // event object. function toMousePosition($event) { - var target = $event.target, - bounds = target.getBoundingClientRect(); + var bounds = subPlotBounds; return { x: $event.clientX - bounds.left, @@ -262,6 +262,7 @@ define( */ hover: function ($event) { isHovering = true; + subPlotBounds = $event.target.getBoundingClientRect(); mousePosition = toMousePosition($event); updateHoverCoordinates(); if (marqueeStart) { @@ -273,11 +274,27 @@ define( updateTicks(); } }, + /** + * Continue a previously-start pan or zoom gesture. + * @param $event the mouse event + */ + continueDrag: function ($event) { + mousePosition = toMousePosition($event); + if (marqueeStart) { + updateMarqueeBox(); + } + if (panStart) { + updatePan(); + updateDrawingBounds(); + updateTicks(); + } + }, /** * Initiate a marquee zoom action. * @param $event the mouse event */ - startMarquee: function ($event) { + startDrag: function ($event) { + subPlotBounds = $event.target.getBoundingClientRect(); mousePosition = toMousePosition($event); if (event.altKey) { // Start panning @@ -301,8 +318,9 @@ define( * Complete a marquee zoom action. * @param $event the mouse event */ - endMarquee: function ($event) { + endDrag: function ($event) { mousePosition = toMousePosition($event); + subPlotBounds = undefined; if (marqueeStart) { marqueeZoom(marqueeStart, mousePosition); marqueeStart = undefined; From e516d886a64f9b3ffa76e6784ca8e3192df79957 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 22 Jun 2015 18:12:25 -0700 Subject: [PATCH 07/21] [Plot] Treat any modifier key as pan Treat any modifier key as a pan gesture when click-dragging in a plot; this is more resilient to cross-platform nuances. WTD-1273. --- platform/features/plot/src/SubPlot.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/features/plot/src/SubPlot.js b/platform/features/plot/src/SubPlot.js index 2d5d2cc43d..a762e4fd81 100644 --- a/platform/features/plot/src/SubPlot.js +++ b/platform/features/plot/src/SubPlot.js @@ -296,7 +296,8 @@ define( startDrag: function ($event) { subPlotBounds = $event.target.getBoundingClientRect(); mousePosition = toMousePosition($event); - if (event.altKey) { + // Treat any modifier key as a pan + if (event.altKey || event.shiftKey || event.ctrlKey) { // Start panning panStart = mousePosition; panStartBounds = panZoomStack.getPanZoom(); From d6a31dcef38bb213ff7723820f061dc397594741 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 23 Jun 2015 14:17:28 -0700 Subject: [PATCH 08/21] [Plot] Update failing specs Update failing specs for changes related to fixed duration plots, WTD-1273. --- .../general/test/directives/MCTDragSpec.js | 19 ++++++++++++------- platform/features/plot/src/SubPlot.js | 2 +- platform/features/plot/test/SubPlotSpec.js | 10 +++++----- .../plot/test/elements/PlotLineBufferSpec.js | 5 +---- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/platform/commonUI/general/test/directives/MCTDragSpec.js b/platform/commonUI/general/test/directives/MCTDragSpec.js index 3f4f1dcadf..1d4568f72b 100644 --- a/platform/commonUI/general/test/directives/MCTDragSpec.js +++ b/platform/commonUI/general/test/directives/MCTDragSpec.js @@ -81,10 +81,11 @@ define( }); it("invokes mctDragDown when dragging begins", function () { - mockElement.on.mostRecentCall.args[1](testEvent(42, 60)); + var event = testEvent(42, 60); + mockElement.on.mostRecentCall.args[1](event); expect(mockScope.$eval).toHaveBeenCalledWith( testAttrs.mctDragDown, - { delta: [0, 0] } + { delta: [0, 0], $event: event } ); }); @@ -101,23 +102,27 @@ define( }); it("invokes mctDrag expression during drag", function () { + var event; + mockElement.on.mostRecentCall.args[1](testEvent(42, 60)); // Find and invoke the mousemove listener mockBody.on.calls.forEach(function (call) { if (call.args[0] === 'mousemove') { - call.args[1](testEvent(52, 200)); + call.args[1](event = testEvent(52, 200)); } }); // Should have passed that delta to mct-drag expression expect(mockScope.$eval).toHaveBeenCalledWith( testAttrs.mctDrag, - { delta: [10, 140] } + { delta: [10, 140], $event: event } ); }); it("invokes mctDragUp expression after drag", function () { + var event; + mockElement.on.mostRecentCall.args[1](testEvent(42, 60)); // Find and invoke the mousemove listener @@ -129,7 +134,7 @@ define( // Find and invoke the mousemove listener mockBody.on.calls.forEach(function (call) { if (call.args[0] === 'mouseup') { - call.args[1](testEvent(40, 71)); + call.args[1](event = testEvent(40, 71)); } }); @@ -138,7 +143,7 @@ define( // initial position expect(mockScope.$eval).toHaveBeenCalledWith( testAttrs.mctDragUp, - { delta: [-2, 11] } + { delta: [-2, 11], $event: event } ); // Should also have unregistered listeners @@ -147,4 +152,4 @@ define( }); } -); \ No newline at end of file +); diff --git a/platform/features/plot/src/SubPlot.js b/platform/features/plot/src/SubPlot.js index a762e4fd81..7c74751b27 100644 --- a/platform/features/plot/src/SubPlot.js +++ b/platform/features/plot/src/SubPlot.js @@ -297,7 +297,7 @@ define( subPlotBounds = $event.target.getBoundingClientRect(); mousePosition = toMousePosition($event); // Treat any modifier key as a pan - if (event.altKey || event.shiftKey || event.ctrlKey) { + if ($event.altKey || $event.shiftKey || $event.ctrlKey) { // Start panning panStart = mousePosition; panStartBounds = panZoomStack.getPanZoom(); diff --git a/platform/features/plot/test/SubPlotSpec.js b/platform/features/plot/test/SubPlotSpec.js index a093b9e289..58cd19faab 100644 --- a/platform/features/plot/test/SubPlotSpec.js +++ b/platform/features/plot/test/SubPlotSpec.js @@ -127,7 +127,7 @@ define( // Simulate a marquee zoom. Note that the mockElement // is 100 by 100 and starts at 10,20 - subplot.startMarquee({ + subplot.startDrag({ target: mockElement, clientX: 60, clientY: 45 @@ -137,7 +137,7 @@ define( clientX: 75, clientY: 85 }); - subplot.endMarquee({ + subplot.endDrag({ target: mockElement, clientX: 80, clientY: 95 @@ -162,7 +162,7 @@ define( // Simulate a marquee zoom. Note that the mockElement // is 100 by 100 and starts at 10,20 - subplot.startMarquee({ + subplot.startDrag({ target: mockElement, clientX: 60, clientY: 45 @@ -172,7 +172,7 @@ define( clientX: 75, clientY: 85 }); - subplot.endMarquee({ + subplot.endDrag({ target: mockElement, clientX: 60, clientY: 45 @@ -198,4 +198,4 @@ define( }); } -); \ No newline at end of file +); diff --git a/platform/features/plot/test/elements/PlotLineBufferSpec.js b/platform/features/plot/test/elements/PlotLineBufferSpec.js index 1066d12d2d..54c0c04c77 100644 --- a/platform/features/plot/test/elements/PlotLineBufferSpec.js +++ b/platform/features/plot/test/elements/PlotLineBufferSpec.js @@ -83,9 +83,6 @@ define( expect(buffer.findInsertionIndex(10)).toEqual(4); expect(buffer.findInsertionIndex(14.5)).toEqual(5); expect(buffer.findInsertionIndex(20)).toEqual(6); - - // 9 is already in there, disallow insertion - expect(buffer.findInsertionIndex(9)).toEqual(-1); }); it("allows insertion in the middle", function () { @@ -169,4 +166,4 @@ define( }); } -); \ No newline at end of file +); From d1dc2da4c8c4df021f5ecbe637ee5c307b79d272 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 23 Jun 2015 14:40:32 -0700 Subject: [PATCH 09/21] [Plot] Remove unused function Remove unused function related to enforcing fixed duration in plots, WTD-1273. --- platform/features/plot/src/elements/PlotUpdater.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index 99d6916b8d..f05632d8f3 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -136,7 +136,7 @@ define( } // Update dimensions and origin based on extrema of plots - function updateExtrema() { + function updateBounds() { if (bufferArray.length > 0) { domainExtrema = bufferArray.map(function (lineBuffer) { return lineBuffer.getDomainExtrema(); @@ -179,7 +179,7 @@ define( (domainExtrema[1] - domainExtrema[0] > fixedDuration)) { cutoff = domainExtrema[1] - fixedDuration; bufferArray.forEach(enforceDurationForBuffer); - updateExtrema(); // Extrema may have changed now + updateBounds(); // Extrema may have changed now } } @@ -194,15 +194,6 @@ define( } } - // Update plot extremea and enforce maximum duration - function updateBounds() { - updateExtrema(); - // Currently not called; this will trim out off-screen - // data from the plot, but doing this will disallow things - // like pan-back, so debatable if we really want to do this - //enforceDuration(); - } - // Handle new telemetry data function update() { var objects = handle.getTelemetryObjects(); From c668fa041af186d241a4b8c55219b4732246c9f7 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 10:20:49 -0700 Subject: [PATCH 10/21] [Core] Allow listening for mutation Allow listeners to register with a domain object's mutation capability to detect changes to that domain object. Allows other components to respond to these changes without resorting to polling on timestamp or similar. WTD-1329. --- .../src/capabilities/MutationCapability.js | 30 +++++++++++++++++-- .../capabilities/MutationCapabilitySpec.js | 22 +++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/platform/core/src/capabilities/MutationCapability.js b/platform/core/src/capabilities/MutationCapability.js index c20c90deab..11f3973e19 100644 --- a/platform/core/src/capabilities/MutationCapability.js +++ b/platform/core/src/capabilities/MutationCapability.js @@ -72,6 +72,13 @@ define( * @constructor */ function MutationCapability(now, domainObject) { + var listeners = []; + + function notifyListeners(model) { + listeners.forEach(function (listener) { + listener(model); + }); + } function mutate(mutator, timestamp) { // Get the object's model and clone it, so the @@ -96,6 +103,7 @@ define( copyValues(model, result); } model.modified = useTimestamp ? timestamp : now(); + notifyListeners(model); } // Report the result of the mutation @@ -107,6 +115,15 @@ define( return fastPromise(mutator(clone)).then(handleMutation); } + function listen(listener) { + listeners.push(listener); + return function unlisten() { + listeners = listeners.filter(function (l) { + return l !== listener; + }); + }; + } + return { /** * Alias of `mutate`, used to support useCapability. @@ -139,10 +156,19 @@ define( * @returns {Promise.} a promise for the result * of the mutation; true if changes were made. */ - mutate: mutate + mutate: mutate, + /** + * Listen for mutations of this domain object's model. + * The provided listener will be invoked with the domain + * object's new model after any changes. To stop listening, + * invoke the function returned by this method. + * @param {Function} listener function to call on mutation + * @returns {Function} a function to stop listening + */ + listen: listen }; } return MutationCapability; } -); \ No newline at end of file +); diff --git a/platform/core/test/capabilities/MutationCapabilitySpec.js b/platform/core/test/capabilities/MutationCapabilitySpec.js index 2dadfa7dfa..99dd3a74c1 100644 --- a/platform/core/test/capabilities/MutationCapabilitySpec.js +++ b/platform/core/test/capabilities/MutationCapabilitySpec.js @@ -83,6 +83,26 @@ define( // Should have gotten a timestamp from 'now' expect(testModel.modified).toEqual(42); }); + + it("notifies listeners of mutation", function () { + var mockCallback = jasmine.createSpy('callback'); + mutation.listen(mockCallback); + mutation.invoke(function (m) { + m.number = 8; + }); + expect(mockCallback).toHaveBeenCalled(); + expect(mockCallback.mostRecentCall.args[0].number) + .toEqual(8); + }); + + it("allows listeners to stop listening", function () { + var mockCallback = jasmine.createSpy('callback'); + mutation.listen(mockCallback)(); // Unlisten immediately + mutation.invoke(function (m) { + m.number = 8; + }); + expect(mockCallback).not.toHaveBeenCalled(); + }); }); } -); \ No newline at end of file +); From b30cbb18ec048157338ba7218c111801a2c9026c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 10:54:02 -0700 Subject: [PATCH 11/21] [Core] Add spec for 'topic' service Add spec for a 'topic' service which will provide the ability for messaging within a local instance of an application. WTD-1329. --- platform/core/test/services/ThrottleSpec.js | 21 +++++++ platform/core/test/services/TopicSpec.js | 70 +++++++++++++++++++++ platform/core/test/suite.json | 3 +- 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 platform/core/test/services/TopicSpec.js diff --git a/platform/core/test/services/ThrottleSpec.js b/platform/core/test/services/ThrottleSpec.js index 173fad8006..bcaf2af363 100644 --- a/platform/core/test/services/ThrottleSpec.js +++ b/platform/core/test/services/ThrottleSpec.js @@ -1,3 +1,24 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ define( diff --git a/platform/core/test/services/TopicSpec.js b/platform/core/test/services/TopicSpec.js new file mode 100644 index 0000000000..b389b19579 --- /dev/null +++ b/platform/core/test/services/TopicSpec.js @@ -0,0 +1,70 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ["../../src/services/Topic"], + function (Topic) { + "use strict"; + + describe("The 'topic' service", function () { + var topic, + testMessage, + mockCallback; + + beforeEach(function () { + testMessage = { someKey: "some value"}; + mockCallback = jasmine.createSpy('callback'); + topic = new Topic(); + }); + + it("notifies listeners on a topic", function () { + topic("abc").listen(mockCallback); + topic("abc").notify(testMessage); + expect(mockCallback).toHaveBeenCalledWith(testMessage); + }); + + it("does not notify listeners across topics", function () { + topic("abc").listen(mockCallback); + topic("xyz").notify(testMessage); + expect(mockCallback).not.toHaveBeenCalledWith(testMessage); + }); + + it("does not notify listeners after unlistening", function () { + topic("abc").listen(mockCallback)(); // Unlisten immediately + topic("abc").notify(testMessage); + expect(mockCallback).not.toHaveBeenCalledWith(testMessage); + }); + + it("provides anonymous private topics", function () { + var t1 = topic(), t2 = topic(); + + t1.listen(mockCallback); + t2.notify(testMessage); + expect(mockCallback).not.toHaveBeenCalledWith(testMessage); + t1.notify(testMessage); + expect(mockCallback).toHaveBeenCalledWith(testMessage); + }); + + }); + } +); diff --git a/platform/core/test/suite.json b/platform/core/test/suite.json index 9c939acf5e..acc7391d02 100644 --- a/platform/core/test/suite.json +++ b/platform/core/test/suite.json @@ -25,6 +25,7 @@ "services/Now", "services/Throttle", + "services/Topic", "types/MergeModels", "types/TypeCapability", @@ -35,4 +36,4 @@ "views/ViewCapability", "views/ViewProvider" -] \ No newline at end of file +] From 1a6d92ee4e06a5c360006bbe37bc5051698201b1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:02:40 -0700 Subject: [PATCH 12/21] [Core] Add topic service Add topic service to support listeners, such as the mutation listener added for WTD-1329. --- platform/core/src/services/Throttle.js | 21 +++++++ platform/core/src/services/Topic.js | 87 ++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 platform/core/src/services/Topic.js diff --git a/platform/core/src/services/Throttle.js b/platform/core/src/services/Throttle.js index 0c86a403c7..c0493a733a 100644 --- a/platform/core/src/services/Throttle.js +++ b/platform/core/src/services/Throttle.js @@ -1,3 +1,24 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ /*global define*/ define( diff --git a/platform/core/src/services/Topic.js b/platform/core/src/services/Topic.js new file mode 100644 index 0000000000..894274b71c --- /dev/null +++ b/platform/core/src/services/Topic.js @@ -0,0 +1,87 @@ +/***************************************************************************** + * Open MCT Web, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT Web is licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Open MCT Web includes source code licensed under additional open source + * licenses. See the Open Source Licenses file (LICENSES.md) included with + * this source code distribution or the Licensing information page available + * at runtime from the About dialog for additional information. + *****************************************************************************/ +/*global define*/ + +define( + [], + function () { + "use strict"; + + /** + * The `topic` service provides a way to create both named, + * shared listeners and anonymous, private listeners. + * + * Usage: + * + * ``` + * var t = topic('foo'); // Use/create a named topic + * t.listen(function () { ... }); + * t.notify({ some: "message" }); + * ``` + * + * Named topics are shared; multiple calls to `topic` + * with the same argument will return a single object instance. + * Anonymous topics (where `topic`has been called with no + * arguments) are private; each call returns a new instance. + * + * @returns {Function} + */ + function Topic() { + var topics = {}; + + function createTopic() { + var listeners = []; + + return { + listen: function (listener) { + listeners.push(listener); + return function unlisten() { + listeners = listeners.filter(function (l) { + return l !== listener; + }); + }; + }, + notify: function (message) { + listeners.forEach(function (listener) { + listener(message); + }); + } + }; + } + + /** + * Use and (if necessary) create a new topic. + * @param {string} [key] name of the topic to use + */ + return function (key) { + if (arguments.length < 1) { + return createTopic(); + } else { + topics[key] = topics[key] || createTopic(); + return topics[key]; + } + }; + } + + return Topic; + } +); From 877461c4a4f27aaa4a3bd5171a1aa174b3383df5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:08:47 -0700 Subject: [PATCH 13/21] [Core] Add dependency to mutation Add the 'topic' dependency to the mutation capability, WTD-1329. --- platform/core/bundle.json | 4 ++-- .../core/src/capabilities/MutationCapability.js | 2 +- .../test/capabilities/MutationCapabilitySpec.js | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/platform/core/bundle.json b/platform/core/bundle.json index 245fc3df72..c4d46511a5 100644 --- a/platform/core/bundle.json +++ b/platform/core/bundle.json @@ -172,7 +172,7 @@ { "key": "mutation", "implementation": "capabilities/MutationCapability.js", - "depends": [ "now" ] + "depends": [ "topic", "now" ] }, { "key": "delegation", @@ -202,4 +202,4 @@ } ] } -} \ No newline at end of file +} diff --git a/platform/core/src/capabilities/MutationCapability.js b/platform/core/src/capabilities/MutationCapability.js index 11f3973e19..a7684e61c2 100644 --- a/platform/core/src/capabilities/MutationCapability.js +++ b/platform/core/src/capabilities/MutationCapability.js @@ -71,7 +71,7 @@ define( * which will expose this capability * @constructor */ - function MutationCapability(now, domainObject) { + function MutationCapability(topic, now, domainObject) { var listeners = []; function notifyListeners(model) { diff --git a/platform/core/test/capabilities/MutationCapabilitySpec.js b/platform/core/test/capabilities/MutationCapabilitySpec.js index 99dd3a74c1..750bf05e48 100644 --- a/platform/core/test/capabilities/MutationCapabilitySpec.js +++ b/platform/core/test/capabilities/MutationCapabilitySpec.js @@ -25,21 +25,30 @@ * MutationCapabilitySpec. Created by vwoeltje on 11/6/14. */ define( - ["../../src/capabilities/MutationCapability"], - function (MutationCapability) { + [ + "../../src/capabilities/MutationCapability", + "../../src/services/Topic" + ], + function (MutationCapability, Topic) { "use strict"; describe("The mutation capability", function () { var testModel, + topic, mockNow, domainObject = { getModel: function () { return testModel; } }, mutation; beforeEach(function () { testModel = { number: 6 }; + topic = new Topic(); mockNow = jasmine.createSpy('now'); mockNow.andReturn(12321); - mutation = new MutationCapability(mockNow, domainObject); + mutation = new MutationCapability( + topic, + mockNow, + domainObject + ); }); it("allows mutation of a model", function () { From e0f672d40dd6fda282030f71724c40ac31927e3c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:12:50 -0700 Subject: [PATCH 14/21] [Core] Add test case for shared listener Add test case for sharing listeners across mutation capability instances, WTD-1329. --- .../src/capabilities/MutationCapability.js | 19 +++++------------ .../capabilities/MutationCapabilitySpec.js | 21 ++++++++++++++++++- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/platform/core/src/capabilities/MutationCapability.js b/platform/core/src/capabilities/MutationCapability.js index a7684e61c2..4268f7c323 100644 --- a/platform/core/src/capabilities/MutationCapability.js +++ b/platform/core/src/capabilities/MutationCapability.js @@ -29,6 +29,8 @@ define( function () { "use strict"; + var TOPIC_PREFIX = "mutation:"; + // Utility function to overwrite a destination object // with the contents of a source object. function copyValues(destination, source) { @@ -72,13 +74,7 @@ define( * @constructor */ function MutationCapability(topic, now, domainObject) { - var listeners = []; - - function notifyListeners(model) { - listeners.forEach(function (listener) { - listener(model); - }); - } + var t = topic(TOPIC_PREFIX + domainObject.getId()); function mutate(mutator, timestamp) { // Get the object's model and clone it, so the @@ -103,7 +99,7 @@ define( copyValues(model, result); } model.modified = useTimestamp ? timestamp : now(); - notifyListeners(model); + t.notify(model); } // Report the result of the mutation @@ -116,12 +112,7 @@ define( } function listen(listener) { - listeners.push(listener); - return function unlisten() { - listeners = listeners.filter(function (l) { - return l !== listener; - }); - }; + return t.listen(listener); } return { diff --git a/platform/core/test/capabilities/MutationCapabilitySpec.js b/platform/core/test/capabilities/MutationCapabilitySpec.js index 750bf05e48..434ddfb098 100644 --- a/platform/core/test/capabilities/MutationCapabilitySpec.js +++ b/platform/core/test/capabilities/MutationCapabilitySpec.js @@ -36,7 +36,10 @@ define( var testModel, topic, mockNow, - domainObject = { getModel: function () { return testModel; } }, + domainObject = { + getId: function () { return "test-id"; }, + getModel: function () { return testModel; } + }, mutation; beforeEach(function () { @@ -112,6 +115,22 @@ define( }); expect(mockCallback).not.toHaveBeenCalled(); }); + + it("shares listeners across instances", function () { + var mockCallback = jasmine.createSpy('callback'), + otherMutation = new MutationCapability( + topic, + mockNow, + domainObject + ); + mutation.listen(mockCallback); + otherMutation.invoke(function (m) { + m.number = 8; + }); + expect(mockCallback).toHaveBeenCalled(); + expect(mockCallback.mostRecentCall.args[0].number) + .toEqual(8); + }); }); } ); From 7a531493d84e34fa2903859e506e46f2b3180677 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:24:43 -0700 Subject: [PATCH 15/21] [Telemetry] Listen for mutation Listen for mutation from telemetry subscriptions, WTD-1329. --- .../telemetry/src/TelemetrySubscription.js | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js index 844f1ab62b..ff512f1e39 100644 --- a/platform/telemetry/src/TelemetrySubscription.js +++ b/platform/telemetry/src/TelemetrySubscription.js @@ -59,6 +59,7 @@ define( telemetryObjects = [], pool = lossless ? new TelemetryQueue() : new TelemetryTable(), metadatas, + unlistenToMutation, updatePending; // Look up domain objects which have telemetry capabilities. @@ -153,16 +154,41 @@ define( return objects; } - // Get a reference to relevant objects (those with telemetry - // capabilities) and subscribe to their telemetry updates. - // Keep a reference to their promised return values, as these - // will be unsubscribe functions. (This must be a promise - // because delegation is supported, and retrieving delegate - // telemetry-capable objects may be an asynchronous operation.) - telemetryObjectPromise = promiseRelevantObjects(domainObject); - unsubscribePromise = telemetryObjectPromise - .then(cacheObjectReferences) - .then(subscribeAll); + function initialize() { + // Get a reference to relevant objects (those with telemetry + // capabilities) and subscribe to their telemetry updates. + // Keep a reference to their promised return values, as these + // will be unsubscribe functions. (This must be a promise + // because delegation is supported, and retrieving delegate + // telemetry-capable objects may be an asynchronous operation.) + telemetryObjectPromise = promiseRelevantObjects(domainObject); + unsubscribePromise = telemetryObjectPromise + .then(cacheObjectReferences) + .then(subscribeAll); + } + + function idsMatch(ids) { + return ids.every(function (id, index) { + return telemetryObjects[index].getId() === id; + }); + } + + function modelChange(model) { + if (!idsMatch((model || {}).composition || [])) { + // Reinitialize if composition has changed + initialize(); + } + } + + function addMutationListener() { + var mutation = domainObject.getCapability('mutation'); + if (mutation) { + return mutation.listen(modelChange); + } + } + + initialize(); + unlistenToMutation = addMutationListener(); return { /** @@ -172,6 +198,9 @@ define( * @memberof TelemetrySubscription */ unsubscribe: function () { + if (unlistenToMutation) { + unlistenToMutation(); + } return unsubscribePromise.then(function (unsubscribes) { return $q.all(unsubscribes.map(function (unsubscribe) { return unsubscribe(); @@ -264,4 +293,4 @@ define( return TelemetrySubscription; } -); \ No newline at end of file +); From f98344b5f646faae051851bfb02f4997b508c915 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:27:20 -0700 Subject: [PATCH 16/21] [Telemetry] Unsubscribe on composition change WTD-1329. --- platform/telemetry/src/TelemetrySubscription.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js index ff512f1e39..f8481eb085 100644 --- a/platform/telemetry/src/TelemetrySubscription.js +++ b/platform/telemetry/src/TelemetrySubscription.js @@ -154,6 +154,14 @@ define( return objects; } + function unsubscribeAll() { + return unsubscribePromise.then(function (unsubscribes) { + return $q.all(unsubscribes.map(function (unsubscribe) { + return unsubscribe(); + })); + }); + } + function initialize() { // Get a reference to relevant objects (those with telemetry // capabilities) and subscribe to their telemetry updates. @@ -176,7 +184,7 @@ define( function modelChange(model) { if (!idsMatch((model || {}).composition || [])) { // Reinitialize if composition has changed - initialize(); + unsubscribeAll().then(initialize); } } @@ -201,11 +209,7 @@ define( if (unlistenToMutation) { unlistenToMutation(); } - return unsubscribePromise.then(function (unsubscribes) { - return $q.all(unsubscribes.map(function (unsubscribe) { - return unsubscribe(); - })); - }); + return unsubscribeAll(); }, /** * Get the most recent domain value that has been observed From f2df0bfdbbc4f8e6211c58e90fffb00c4070bfde Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:53:43 -0700 Subject: [PATCH 17/21] [Core] Expose 'topic' service Expose 'topic' services, which supports updating telemetry subscriptions on mutation for WTD-1329. --- platform/core/bundle.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/core/bundle.json b/platform/core/bundle.json index c4d46511a5..c20a9dc2e2 100644 --- a/platform/core/bundle.json +++ b/platform/core/bundle.json @@ -189,6 +189,10 @@ "key": "throttle", "implementation": "services/Throttle.js", "depends": [ "$timeout" ] + }, + { + "key": "topic", + "implementation": "services/Topic.js" } ], "roots": [ From bcfe80fbdce44ab56b0d7f8fbfe7920bf354fa81 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 12:54:49 -0700 Subject: [PATCH 18/21] [Plot] Handle changes to telemetry objects Handle changes to the set of visible telemetry objects in a plot; addresses WTD-619, which is related to WTD-1329. --- .../features/plot/src/elements/PlotUpdater.js | 28 +++++++++---------- .../telemetry/src/TelemetrySubscription.js | 10 ++++--- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/platform/features/plot/src/elements/PlotUpdater.js b/platform/features/plot/src/elements/PlotUpdater.js index df7eef9abc..e31641f7c7 100644 --- a/platform/features/plot/src/elements/PlotUpdater.js +++ b/platform/features/plot/src/elements/PlotUpdater.js @@ -50,6 +50,7 @@ define( origin = [0, 0], domainExtrema, rangeExtrema, + buffers = {}, bufferArray = [], domainOffset; @@ -61,11 +62,10 @@ define( // Check if this set of ids matches the current set of ids // (used to detect if line preparation can be skipped) function idsMatch(nextIds) { - return nextIds.map(function (id, index) { - return ids[index] === id; - }).reduce(function (a, b) { - return a && b; - }, true); + return ids.length === nextIds.length && + nextIds.every(function (id, index) { + return ids[index] === id; + }); } // Prepare plot lines for this group of telemetry objects @@ -74,7 +74,7 @@ define( next = {}; // Detect if we already have everything we need prepared - if (ids.length === nextIds.length && idsMatch(nextIds)) { + if (idsMatch(nextIds)) { // Nothing to prepare, move on return; } @@ -88,13 +88,13 @@ define( // Create buffers for these objects bufferArray = ids.map(function (id) { - var buffer = new PlotLineBuffer( - domainOffset, - INITIAL_SIZE, - maxPoints - ); - next[id] = lines[id] || new PlotLine(buffer); - return buffer; + buffers[id] = buffers[id] || new PlotLineBuffer( + domainOffset, + INITIAL_SIZE, + maxPoints + ); + next[id] = lines[id] || new PlotLine(buffers[id]); + return buffers[id]; }); } @@ -290,4 +290,4 @@ define( return PlotUpdater; } -); \ No newline at end of file +); diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js index f8481eb085..69fd957ac2 100644 --- a/platform/telemetry/src/TelemetrySubscription.js +++ b/platform/telemetry/src/TelemetrySubscription.js @@ -147,7 +147,8 @@ define( telemetryObjects = objects; metadatas = objects.map(lookupMetadata); // Fire callback, as this will be the first time that - // telemetry objects are available + // telemetry objects are available, or these objects + // will have changed. if (callback) { callback(); } @@ -176,9 +177,10 @@ define( } function idsMatch(ids) { - return ids.every(function (id, index) { - return telemetryObjects[index].getId() === id; - }); + return ids.length === telemetryObjects.length && + ids.every(function (id, index) { + return telemetryObjects[index].getId() === id; + }); } function modelChange(model) { From 360c3fcf1562dc1553897efc6db619eb072cb87b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 13:17:42 -0700 Subject: [PATCH 19/21] [Telemetry] Update specs Update specs to reflect changes for WTD-1329. --- platform/telemetry/src/TelemetrySubscription.js | 3 ++- .../telemetry/test/TelemetrySubscriptionSpec.js | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/platform/telemetry/src/TelemetrySubscription.js b/platform/telemetry/src/TelemetrySubscription.js index 69fd957ac2..808d8c5c5d 100644 --- a/platform/telemetry/src/TelemetrySubscription.js +++ b/platform/telemetry/src/TelemetrySubscription.js @@ -191,7 +191,8 @@ define( } function addMutationListener() { - var mutation = domainObject.getCapability('mutation'); + var mutation = domainObject && + domainObject.getCapability('mutation'); if (mutation) { return mutation.listen(modelChange); } diff --git a/platform/telemetry/test/TelemetrySubscriptionSpec.js b/platform/telemetry/test/TelemetrySubscriptionSpec.js index 458a2f685e..e1c834e6cd 100644 --- a/platform/telemetry/test/TelemetrySubscriptionSpec.js +++ b/platform/telemetry/test/TelemetrySubscriptionSpec.js @@ -32,6 +32,7 @@ define( mockDomainObject, mockCallback, mockTelemetry, + mockMutation, mockUnsubscribe, mockSeries, testMetadata, @@ -59,6 +60,10 @@ define( "telemetry", ["subscribe", "getMetadata"] ); + mockMutation = jasmine.createSpyObj( + "mutation", + ["mutate", "listen"] + ); mockUnsubscribe = jasmine.createSpy("unsubscribe"); mockSeries = jasmine.createSpyObj( "series", @@ -68,7 +73,12 @@ define( mockQ.when.andCallFake(mockPromise); mockDomainObject.hasCapability.andReturn(true); - mockDomainObject.getCapability.andReturn(mockTelemetry); + mockDomainObject.getCapability.andCallFake(function (c) { + return { + telemetry: mockTelemetry, + mutation: mockMutation + }[c]; + }); mockDomainObject.getId.andReturn('test-id'); mockTelemetry.subscribe.andReturn(mockUnsubscribe); @@ -215,4 +225,4 @@ define( }); }); } -); \ No newline at end of file +); From befbcab892d6cb06b737c068b23bdbaac846a84d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 24 Jun 2015 13:25:05 -0700 Subject: [PATCH 20/21] [Telmetry] Add test cases Add test cases for TelemetrySubscription related to listening for mutation, to support views of domain objects which delegate telemetry, for WTD-1329. --- .../test/TelemetrySubscriptionSpec.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/platform/telemetry/test/TelemetrySubscriptionSpec.js b/platform/telemetry/test/TelemetrySubscriptionSpec.js index e1c834e6cd..1715504b6e 100644 --- a/platform/telemetry/test/TelemetrySubscriptionSpec.js +++ b/platform/telemetry/test/TelemetrySubscriptionSpec.js @@ -34,6 +34,7 @@ define( mockTelemetry, mockMutation, mockUnsubscribe, + mockUnlisten, mockSeries, testMetadata, subscription; @@ -65,6 +66,7 @@ define( ["mutate", "listen"] ); mockUnsubscribe = jasmine.createSpy("unsubscribe"); + mockUnlisten = jasmine.createSpy("unlisten"); mockSeries = jasmine.createSpyObj( "series", [ "getPointCount", "getDomainValue", "getRangeValue" ] @@ -84,6 +86,8 @@ define( mockTelemetry.subscribe.andReturn(mockUnsubscribe); mockTelemetry.getMetadata.andReturn(testMetadata); + mockMutation.listen.andReturn(mockUnlisten); + mockSeries.getPointCount.andReturn(42); mockSeries.getDomainValue.andReturn(123456); mockSeries.getRangeValue.andReturn(789); @@ -223,6 +227,22 @@ define( expect(mockCallback2) .toHaveBeenCalledWith([ mockDomainObject ]); }); + + it("reinitializes on mutation", function () { + expect(mockTelemetry.subscribe.calls.length).toEqual(1); + // Notify of a mutation which appears to change composition + mockMutation.listen.mostRecentCall.args[0]({ + composition: ['Z'] + }); + // Use subscribe call as an indication of reinitialization + expect(mockTelemetry.subscribe.calls.length).toEqual(2); + }); + + it("stops listening for mutation on unsubscribe", function () { + expect(mockUnlisten).not.toHaveBeenCalled(); + subscription.unsubscribe(); + expect(mockUnlisten).toHaveBeenCalled(); + }); }); } ); From e58209284404b97032284a8032b470433c57367c Mon Sep 17 00:00:00 2001 From: larkin Date: Fri, 26 Jun 2015 15:23:22 -0700 Subject: [PATCH 21/21] [Comments] Add missing space --- platform/core/src/services/Topic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core/src/services/Topic.js b/platform/core/src/services/Topic.js index 894274b71c..f1afafa843 100644 --- a/platform/core/src/services/Topic.js +++ b/platform/core/src/services/Topic.js @@ -40,7 +40,7 @@ define( * * Named topics are shared; multiple calls to `topic` * with the same argument will return a single object instance. - * Anonymous topics (where `topic`has been called with no + * Anonymous topics (where `topic` has been called with no * arguments) are private; each call returns a new instance. * * @returns {Function}