diff --git a/package.json b/package.json index 56688244f..763612b1c 100644 --- a/package.json +++ b/package.json @@ -246,7 +246,7 @@ "opensphere-build-docs": "^1.0.0", "opensphere-build-index": "^1.0.0", "opensphere-build-resolver": "^5.0.0", - "opensphere-state-schema": "^2.1.0", + "opensphere-state-schema": "^2.3.0", "postcss-cli": "^5.0.0", "replace": "^0.3.0", "rimraf": "^2.5.4", diff --git a/src/os/state/v4/timestate.js b/src/os/state/v4/timestate.js index 58e37bff9..7ac1e4ebb 100644 --- a/src/os/state/v4/timestate.js +++ b/src/os/state/v4/timestate.js @@ -37,7 +37,12 @@ os.state.v4.TimeTag = { HOLDS: 'heldIntervals', HOLD_ITEM: 'held', SEQ_INTERVAL: 'interval', - TIME: 'time' + TIME: 'time', + SLICES: 'slices', + SLICE: 'slice', + SLICE_INTERVAL: 'sliceInterval', + INTERVAL_START: 'intervalStart', + INTERVAL_END: 'intervalEnd' }; @@ -170,6 +175,7 @@ os.state.v4.TimeState.prototype.loadInternal = function(obj, id) { } tlc.setHoldRanges(this.readIntervalsAsRangeSet_(obj, os.state.v4.TimeTag.HOLDS)); + tlc.setSliceRanges(this.readSlicesAsRangeSet_(obj)); } // set the active window position. this needs to be called after auto configure, or these values will be overridden. @@ -255,7 +261,7 @@ os.state.v4.TimeState.prototype.saveInternal = function(options, rootObj) { // NOTE: v4 heldIntervals can include an optional key element // which should be associated with a specific layer. Currently, // we do not support this feature, so the following - // just reaads all the interval elements. + // just reads all the interval elements. rootObj.appendChild(this.holdRangeToXml_(tlc.getHoldRanges())); } @@ -268,6 +274,10 @@ os.state.v4.TimeState.prototype.saveInternal = function(options, rootObj) { os.xml.appendElement(os.state.v4.TimeTag.PLAY_STATE, animation, playState); } + if (tlc.hasSliceRanges()) { + rootObj.appendChild(this.sliceRangesToXml_(tlc.getSliceRanges())); + } + os.xml.appendElement(os.state.v4.TimeTag.DURATION, rootObj, tlc.getDuration()); this.saveComplete(options, rootObj); @@ -405,7 +415,7 @@ os.state.v4.TimeState.prototype.readDuration_ = function(element, range) { /** - * Reads a collection of intervals and retruns a RangeSet + * Reads a collection of intervals and returns a RangeSet * @param {!Element} element * @param {string} tag * @return {goog.math.RangeSet} @@ -520,3 +530,57 @@ os.state.v4.TimeState.prototype.parsePeriod = function(period) { return null; }; + +/** + * Returns slice intervals element for timeranges. + * @param {Array} sliceRanges + * @return {!Element} + * @private + */ +os.state.v4.TimeState.prototype.sliceRangesToXml_ = function(sliceRanges) { + var slices = os.xml.createElement(os.state.v4.TimeTag.SLICES); + + for (var i = 0; i < sliceRanges.length; i++) { + var slice = os.xml.createElement(os.state.v4.TimeTag.SLICE); + slices.appendChild(slice); + var interval = os.xml.createElement(os.state.v4.TimeTag.SLICE_INTERVAL); + slice.appendChild(interval); + var range = sliceRanges[i]; + os.xml.appendElement(os.state.v4.TimeTag.INTERVAL_START, interval, range.start); + os.xml.appendElement(os.state.v4.TimeTag.INTERVAL_END, interval, range.end); + } + return slices; +}; + +/** + * Reads a collection of slices and returns a RangeSet + * @param {!Element} element + * @return {goog.math.RangeSet} + * @private + */ +os.state.v4.TimeState.prototype.readSlicesAsRangeSet_ = function(element) { + var rangeSet = new goog.math.RangeSet(); + if (element) { + var slicesElement = element.querySelector(os.state.v4.TimeTag.SLICES); + if (slicesElement) { + var intervals = slicesElement.querySelectorAll(os.state.v4.TimeTag.SLICE_INTERVAL); + for (var i = 0; i < intervals.length; i = i + 1) { + var interval = intervals[i]; + rangeSet.add(this.sliceIntervalToRange_(interval)); + } + } + } + return rangeSet; +}; + +/** + * Returns a range for a given slice interval + * @param {!Element} interval + * @return {goog.math.Range} + * @private + */ +os.state.v4.TimeState.prototype.sliceIntervalToRange_ = function(interval) { + var intervalStart = +interval.querySelector(os.state.v4.TimeTag.INTERVAL_START).textContent; + var intervalEnd = +interval.querySelector(os.state.v4.TimeTag.INTERVAL_END).textContent; + return new goog.math.Range(intervalStart, intervalEnd); +}; diff --git a/test/os.xsd.mock.js b/test/os.xsd.mock.js index ac36514ba..94023886d 100644 --- a/test/os.xsd.mock.js +++ b/test/os.xsd.mock.js @@ -52,7 +52,7 @@ os.test.xsd.loadStateXsdFiles = function() { var stateFileRoot = '/opensphere-state-schema/src/main/xsd/'; var p = goog.Promise.withResolver(); - // retun the cached result, if it exists. + // return the cached result, if it exists. if (os.test.xsd.stateCache_) { p.resolve(os.test.xsd.stateCache_); return p.promise; diff --git a/test/os/state/v4/timestate.test.js b/test/os/state/v4/timestate.test.js index 7a8481966..f0e4f9acf 100644 --- a/test/os/state/v4/timestate.test.js +++ b/test/os/state/v4/timestate.test.js @@ -40,10 +40,16 @@ describe('os.state.v4.TimeState', function() { var holdEndDate = os.time.parseMoment('1971-06-12T01:15:11Z', [os.state.v4.TimeState.DATE_FORMAT], true); var holdRange = new goog.math.Range(holdStartDate.valueOf(), holdEndDate.valueOf()); - // make sure this is empty bfeore the test. + // make sure this is empty before the test. tlc.clearHoldRanges(); tlc.addHoldRange(holdRange); + // Slice ranges + var sliceRange1 = new goog.math.Range(20, 50); + var sliceRange2 = new goog.math.Range(20000, 5000000); + tlc.addSliceRange(sliceRange1); + tlc.addSliceRange(sliceRange2); + tlc.setCurrent(animateEndDate.valueOf()); tlc.setFps(1); tlc.setOffset(1000002); @@ -53,6 +59,7 @@ describe('os.state.v4.TimeState', function() { spyOn(tlc, 'setDuration').andCallThrough(); spyOn(tlc, 'setAnimateRanges').andCallThrough(); spyOn(tlc, 'setHoldRanges').andCallThrough(); + spyOn(tlc, 'setSliceRanges').andCallThrough(); spyOn(tlc, 'setCurrent').andCallThrough(); spyOn(tlc, 'setSkip').andCallThrough(); spyOn(tlc, 'setFps').andCallThrough(); @@ -76,7 +83,7 @@ describe('os.state.v4.TimeState', function() { // waiting for the xsd files to load waitsFor(function() { return (resultSchemas !== null); - }, 'Wait for XSD(s) to laod', 2 * jasmine.DEFAULT_TIMEOUT_INTERVAL); + }, 'Wait for XSD(s) to load', 2 * jasmine.DEFAULT_TIMEOUT_INTERVAL); // Runs the tests. runs(function() { @@ -89,6 +96,7 @@ describe('os.state.v4.TimeState', function() { var duration = tlc.getDuration(); var aRanges = tlc.animateRanges_.clone(); var hRanges = tlc.holdRanges_.clone(); + var sRanges = tlc.sliceRanges_.clone(); var current = tlc.getCurrent(); var skip = tlc.getSkip(); var fps = tlc.getFps(); @@ -111,7 +119,7 @@ describe('os.state.v4.TimeState', function() { expect(tlc.setDuration).toHaveBeenCalled(); // When the timeline controler is loaded from a state, it goes // through an auto-configuration step that computes a - // good duration, I believe that a ticket was written againt + // good duration, I believe that a ticket was written against // using the stored value...? expect(tlc.setDuration.mostRecentCall.args[0]).not.toBe(duration); @@ -123,6 +131,10 @@ describe('os.state.v4.TimeState', function() { expect(goog.math.RangeSet.equals(hRanges, tlc.setHoldRanges.mostRecentCall.args[0])).toBe(true); + expect(tlc.setSliceRanges).toHaveBeenCalled(); + expect(goog.math.RangeSet.equals(sRanges, + tlc.setSliceRanges.mostRecentCall.args[0])).toBe(true); + expect(tlc.setCurrent).toHaveBeenCalled(); expect(tlc.setCurrent.mostRecentCall.args[0]).toBe(current);