diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java index 93d7ad1ef07..e2aca707077 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedRacesManagementPanel.java @@ -61,12 +61,14 @@ public class TrackedRacesManagementPanel extends AbstractEventManagementPanel im selectedRacePanel.setContentWidget(vPanel); selectedRacePanel.setVisible(false); - raceDataGrid = new Grid(3,2); + raceDataGrid = new Grid(5,2); vPanel.add(raceDataGrid); raceDataGrid.setText(0, 0, "StartTime:"); raceDataGrid.setText(1, 0, "EndTime:"); raceDataGrid.setText(2, 0, "Duration:"); + raceDataGrid.setText(3, 0, "Start of tracking:"); + raceDataGrid.setText(4, 0, "End of tracking:"); } @Override @@ -117,6 +119,12 @@ public class TrackedRacesManagementPanel extends AbstractEventManagementPanel im } else { raceDataGrid.setText(2, 1, ""); } + if(selectedRaceDTO.startOfTracking != null) { + raceDataGrid.setText(3, 1, dateFormatter.render(selectedRaceDTO.startOfTracking) + " " + + timeFormatter.render(selectedRaceDTO.startOfTracking)); + } else { + raceDataGrid.setText(3, 1, ""); + } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java index 8b6f7593a99..4a996da4b8e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RaceTimePanel.java @@ -75,6 +75,14 @@ public class RaceTimePanel extends TimePanel implements R } } + @Override + public void onTimeZoom(Date zoomStartTimepoint, Date zoomEndTimepoint) { + } + + @Override + public void onTimeZoomReset() { + } + @Override protected boolean isLiveModeToBeMadePossible() { long livePlayDelayInMillis = timer.getLivePlayDelayInMillis(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java index 4087eb4a3f9..77ea8ec91ba 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java @@ -28,8 +28,10 @@ import com.sap.sailing.gwt.ui.shared.components.Component; import com.sap.sailing.gwt.ui.shared.components.SettingsDialog; import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent; import com.sap.sailing.gwt.ui.shared.controls.slider.SliderBar; +import com.sap.sailing.gwt.ui.shared.controls.slider.TimeSlider; -public class TimePanel extends FormPanel implements Component, TimeListener, PlayStateListener, RequiresResize { +public class TimePanel extends FormPanel implements Component, TimeListener, TimeZoomChangeListener, + PlayStateListener, RequiresResize { protected final Timer timer; /** @@ -105,7 +107,7 @@ public class TimePanel extends FormPanel implements playModeInactiveImg = resources.timesliderPlayStateLiveInactiveIcon(); playModeImage = new Image(playModeInactiveImg); - sliderBar = new SliderBar(); + sliderBar = new TimeSlider(); sliderBar.setEnabled(true); sliderBar.setLabelFormatter(new SliderBar.LabelFormatter() { final DateTimeFormat formatter = DateTimeFormat.getFormat("HH:mm"); @@ -306,6 +308,7 @@ public class TimePanel extends FormPanel implements */ public void setMinMax(Date min, Date max, boolean fireEvent) { assert min != null && max != null; + boolean changed = false; int numTicks = 8; if (!max.equals(this.max)) { @@ -381,6 +384,14 @@ public class TimePanel extends FormPanel implements } } + @Override + public void onTimeZoom(Date zoomStartTimepoint, Date zoomEndTimepoint) { + } + + @Override + public void onTimeZoomReset() { + } + protected boolean isLiveModeToBeMadePossible() { return false; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 960252e3ce1..537f9e33877 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -1,7 +1,6 @@ package com.sap.sailing.gwt.ui.raceboard; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -12,7 +11,6 @@ import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.sap.sailing.domain.common.DefaultLeaderboardName; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.gwt.ui.client.AbstractEntryPoint; import com.sap.sailing.gwt.ui.client.ParallelExecutionCallback; import com.sap.sailing.gwt.ui.client.ParallelExecutionHolder; @@ -25,7 +23,6 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; import com.sap.sailing.gwt.ui.shared.RaceDTO; import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sailing.gwt.ui.shared.UserDTO; -import com.sap.sailing.gwt.ui.shared.panels.BreadcrumbPanel; public class RaceBoardEntryPoint extends AbstractEntryPoint { private RaceDTO selectedRace; @@ -154,35 +151,6 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint { return null; } - private FlowPanel createBreadcrumbPanel() { - FlowPanel raceBoardHeaderPanel = new FlowPanel(); - raceBoardHeaderPanel.addStyleName("RaceBoardHeaderPanel"); - - List> links = new ArrayList>(); - String debugParam = Window.Location.getParameter("gwt.codesvr"); - String root = Window.Location.getParameter("root"); - if (leaderboardGroupName != null && !leaderboardGroupName.isEmpty()) { - if (root.equals("overview")) { - String link = "/gwt/Spectator.html" - + (debugParam != null && !debugParam.isEmpty() ? "?gwt.codesvr=" + debugParam : ""); - links.add(new Pair(link, stringMessages.home())); - } - String link = "/gwt/Spectator.html?leaderboardGroupName=" + leaderboardGroupName - + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : ""); - links.add(new Pair(link, leaderboardGroupName)); - } - if (leaderboardName != null && !leaderboardName.isEmpty()) { - String link = "/gwt/Leaderboard.html?name=" + leaderboardName - + (leaderboardGroupName != null ? "&leaderboardGroupName=" + leaderboardGroupName : "") - + "&root=" + root + (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : ""); - links.add(new Pair(link, leaderboardName)); - } - BreadcrumbPanel breadcrumbPanel = new BreadcrumbPanel(links, raceName); - raceBoardHeaderPanel.add(breadcrumbPanel); - - return raceBoardHeaderPanel; - } - private FlowPanel createTimePanel(RaceBoardPanel raceBoardPanel) { FlowPanel timeLineInnerBgPanel = new FlowPanel(); timeLineInnerBgPanel.addStyleName("timeLineInnerBgPanel"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 948e7e87e58..28b72996238 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -261,8 +261,7 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS componentsNavigationPanel.add(checkBox); } - - private void addComponentAsToogleButtonToNavigationMenu(final ComponentViewer componentViewer, + public void addComponentAsToogleButtonToNavigationMenu(final ComponentViewer componentViewer, final Component component) { final ToggleButton toggleButton = new ToggleButton(component.getLocalizedShortName(), "\u2713 " + component.getLocalizedShortName()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/WindChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/WindChart.java index 474f0d0fea9..59d19a44ce0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/WindChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/charts/WindChart.java @@ -136,8 +136,7 @@ public class WindChart extends RaceChart implements Component chart.getXAxis().setType(Axis.Type.DATE_TIME) .setMaxZoom(60 * 1000) // 1 minute - .setAxisTitleText(stringMessages.time()) - .setTickInterval(1000 * 60 * 10); + .setAxisTitleText(stringMessages.time()); chart.getXAxis().setLabels(new XAxisLabels().setFormatter(new AxisLabelsFormatter() { @Override public String format(AxisLabelsData axisLabelsData) { @@ -322,7 +321,6 @@ public class WindChart extends RaceChart implements Component for (WindDTO wind : windTrackInfo.windFixes) { - Date windDate = new Date(wind.originTimepoint.longValue()); if (timeOfEarliestRequestInMillis == null || wind.timepoint tickLabelElements = new ArrayList(); + protected List tickLabelElements = new ArrayList(); /** * The elements used to display the marker labels. */ - private List markerLabelElements = new ArrayList(); + protected List markerLabelElements = new ArrayList(); /** * The formatter used to generate label text. */ - private LabelFormatter tickLabelFormatter; + protected LabelFormatter tickLabelFormatter; /** * The line that the knob moves over. */ - private Element lineElement; + protected Element lineElement; /** * The offset between the edge of the shell and the line. */ - private int lineLeftOffset = 0; + protected int lineLeftOffset = 0; /** * The maximum slider value. */ - private Double maxValue; + protected Double maxValue; /** * The minimum slider value. */ - private Double minValue; + protected Double minValue; /** * The number of labels to show. @@ -250,7 +250,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue tickElements = new ArrayList(); + protected List tickElements = new ArrayList(); /** * The elements used to display additional markers on the slider bar. @@ -863,7 +863,7 @@ public class SliderBar extends FocusPanel implements RequiresResize, HasValue 0) { int lineWidth = lineElement.getOffsetWidth(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/TimeSlider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/TimeSlider.java new file mode 100644 index 00000000000..1ef41444a91 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/TimeSlider.java @@ -0,0 +1,120 @@ +package com.sap.sailing.gwt.ui.shared.controls.slider; + +import java.util.List; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.sap.sailing.gwt.ui.shared.controls.slider.TimeTicksCalculator.NormalizedInterval; +import com.sap.sailing.gwt.ui.shared.controls.slider.TimeTicksCalculator.TickPosition; + +public class TimeSlider extends SliderBar { + + private final TimeTicksCalculator calc = new TimeTicksCalculator(); + + /** + * Draw the tick along the line. + */ + protected void drawTicks() { + if (!isAttached() || !isMinMaxInitialized()) + return; + + NormalizedInterval normalizedTimeTickInterval = calc.normalizeTimeTickInterval((maxValue.longValue() - minValue.longValue()) / 8); + List calculatedTimeTicks = calc.calculateTimeTicks(normalizedTimeTickInterval, minValue.longValue(), maxValue.longValue(), 1); + + // Draw the ticks + int lineWidth = lineElement.getOffsetWidth(); + // Create the ticks or make them visible + for (int i = 0; i < calculatedTimeTicks.size(); i++) { + TickPosition tickPosition = calculatedTimeTicks.get(i); + + Element tick = null; + if (i < tickElements.size()) { + tick = tickElements.get(i); + } else { // Create the new tick + tick = DOM.createDiv(); + DOM.setStyleAttribute(tick, "position", "absolute"); + DOM.setStyleAttribute(tick, "display", "none"); + DOM.appendChild(getElement(), tick); + tickElements.add(tick); + } + if (enabled) { + DOM.setElementProperty(tick, "className", "gwt-SliderBar-tick"); + } else { + DOM.setElementProperty(tick, "className", "gwt-SliderBar-tick gwt-SliderBar-tick-disabled"); + } + // Position the tick and make it visible + DOM.setStyleAttribute(tick, "visibility", "hidden"); + DOM.setStyleAttribute(tick, "display", ""); + int tickWidth = tick.getOffsetWidth(); + long pos = (tickPosition.time.getTime() - minValue.longValue()) * lineWidth / (maxValue.longValue() - minValue.longValue()); + int tickLeftOffset = lineLeftOffset + (int) pos - (tickWidth / 2); + tickLeftOffset = Math.min(tickLeftOffset, lineLeftOffset + lineWidth - tickWidth); + DOM.setStyleAttribute(tick, "left", tickLeftOffset + "px"); + DOM.setStyleAttribute(tick, "visibility", "visible"); + } + + // Hide unused ticks + for (int i = (calculatedTimeTicks.size() + 1); i < tickElements.size(); i++) { + DOM.setStyleAttribute(tickElements.get(i), "display", "none"); + } + } + + + /** + * Draw the labels along the line. + */ + protected void drawTickLabels() { + if (!isAttached() || !isMinMaxInitialized()) + return; + + NormalizedInterval normalizedTimeTickInterval = calc.normalizeTimeTickInterval((maxValue.longValue() - minValue.longValue()) / 8); + List calculatedTimeTicks = calc.calculateTimeTicks(normalizedTimeTickInterval, minValue.longValue(), maxValue.longValue(), 1); + + // Draw the tick labels + int lineWidth = lineElement.getOffsetWidth(); + // Create the labels or make them visible + for (int i = 0; i < calculatedTimeTicks.size(); i++) { + TickPosition tickPosition = calculatedTimeTicks.get(i); + Element label = null; + if (i < tickLabelElements.size()) { + label = tickLabelElements.get(i); + } else { // Create the new label + label = DOM.createDiv(); + DOM.setStyleAttribute(label, "position", "absolute"); + DOM.setStyleAttribute(label, "display", "none"); + if (enabled) { + DOM.setElementProperty(label, "className", "gwt-SliderBar-ticklabel"); + } else { + DOM.setElementProperty(label, "className", "gwt-SliderBar-ticklabel-disabled"); + } + DOM.appendChild(getElement(), label); + tickLabelElements.add(label); + } + + // Set the label text + double value = tickPosition.time.getTime(); + DOM.setStyleAttribute(label, "visibility", "hidden"); + DOM.setStyleAttribute(label, "display", ""); + DOM.setElementProperty(label, "innerHTML", formatTickLabel(value)); + + // Move to the left so the label width is not clipped by the + // shell + DOM.setStyleAttribute(label, "left", "0px"); + + // Position the label and make it visible + int labelWidth = label.getOffsetWidth(); + long pos = (tickPosition.time.getTime() - minValue.longValue()) * lineWidth / (maxValue.longValue() - minValue.longValue()); + int labelLeftOffset = lineLeftOffset + (int) pos - (labelWidth / 2); + labelLeftOffset = Math.min(labelLeftOffset, lineLeftOffset + lineWidth - labelWidth); + labelLeftOffset = Math.max(labelLeftOffset, lineLeftOffset); + DOM.setStyleAttribute(label, "left", labelLeftOffset + "px"); + DOM.setStyleAttribute(label, "visibility", "visible"); + } + + // Hide unused labels + for (int i = (calculatedTimeTicks.size() + 1); i < tickLabelElements.size(); i++) { + DOM.setStyleAttribute(tickLabelElements.get(i), "display", "none"); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/TimeTicksCalculator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/TimeTicksCalculator.java new file mode 100644 index 00000000000..a741ded3f4a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/controls/slider/TimeTicksCalculator.java @@ -0,0 +1,243 @@ +package com.sap.sailing.gwt.ui.shared.controls.slider; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class TimeTicksCalculator { + + public TimeTicksCalculator() { + } + + /** + * Get a normalized tick interval for dates. Returns a configuration object with unit range (interval), count and + * name. Used to prepare data for getTimeTicks. Previously this logic was part of getTimeTicks, but as getTimeTicks + * now runs of segments in stock charts, the normalizing logic was extracted in order to prevent it for running over + * again for each segment having the same interval. + */ + public NormalizedInterval normalizeTimeTickInterval(long tickInterval) { + TimeUnits[] units = TimeUnits.values(); + + TimeUnits unit = units[units.length - 1]; // default unit is years + long interval = unit.unitInMs; + int[] multiples = unit.allowedMultiples; + int count; + + // loop through the units to find the one that best fits the tickInterval + for (int i = 0; i < units.length; i++) { + unit = units[i]; + interval = unit.unitInMs; + multiples = unit.allowedMultiples; + + if (i + 1 < units.length) { + // lessThan is in the middle between the highest multiple and the next unit. + long lessThan = (interval * multiples[multiples.length - 1] + units[i + 1].unitInMs) / 2; + + // break and keep the current unit + if (tickInterval <= lessThan) { + break; + } + } + } + + // prevent 2.5 years intervals, though 25, 250 etc. are allowed + if (interval == TimeUnits.YEAR.unitInMs && tickInterval < 5 * interval) { + multiples = new int[] { 1, 2, 5 }; + } + + // get the count + count = (int) normalizeTickInterval(tickInterval / interval, multiples, 1); + + return new NormalizedInterval(unit.name(), interval, count); + } + + /** + * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5 + * + * @param {Number} interval + * @param {Array} multiples + * @param {Number} magnitude + * @param {Object} options + */ + public long normalizeTickInterval(long interval, int[] multiples, int magnitude) { + long normalized; + + // round to a tenfold of 1, 2, 2.5 or 5 + normalized = interval / magnitude; + + // multiples for a linear scale + if (magnitude == 1) { + multiples = new int[] { 1, 2, 5, 10 }; + } + + // normalize the interval to the nearest multiple + for (int i = 0; i < multiples.length; i++) { + interval = multiples[i]; + int addedValue = i + 1 < multiples.length ? multiples[i + 1] : multiples[i]; + if (normalized <= (multiples[i] + addedValue) / 2) { + break; + } + } + + // multiply back to the correct magnitude + interval *= magnitude; + + return interval; + } + + private long makeTime(int year, int month) { + return makeTime(year, month, 1, 0, 0, 0); + } + + private long makeTime(int year, int month, int date) { + return makeTime(year, month, date, 0, 0, 0); + } + + @SuppressWarnings("deprecation") + private long makeTime(int year, int month, int date, int hours, int minutes, int seconds) { + return new Date(year, month, date, hours, minutes, seconds).getTime(); + } + + /** + * Set the tick positions to a time unit that makes sense, for example on the first of each month or on every + * Monday. Return an array with the time positions. Used in datetime axes as well as for grouping data on a datetime + * axis. + * + * @param {Object} normalizedInterval The interval in axis values (ms) and the count + * @param {Number} min The minimum in axis values + * @param {Number} max The maximum in axis values + * @param {Number} startOfWeek + */ + @SuppressWarnings("deprecation") + public List calculateTimeTicks(NormalizedInterval normalizedInterval, long min, long max, + long startOfWeek) { + List tickPositions = new ArrayList(); + int i; + boolean useUTC = true; + int minYear = 0; // used in months and years as a basis for Date.UTC() + Date minDate = new Date(min); + long interval = normalizedInterval.unitRange; + int count = (int) normalizedInterval.count; + + + if (interval >= TimeUnits.SECOND.unitInMs) { // second + minDate.setTime(min - (min % 1000)); + minDate.setSeconds(interval >= TimeUnits.MINUTE.unitInMs ? 0 : count + * Math.round(minDate.getSeconds() / count)); + } + + if (interval >= TimeUnits.MINUTE.unitInMs) { // minute + minDate.setMinutes(interval >= TimeUnits.HOUR.unitInMs ? 0 : count + * Math.round(minDate.getMinutes() / count)); + } + + if (interval >= TimeUnits.HOUR.unitInMs) { // hour + minDate.setHours(interval >= TimeUnits.DAY.unitInMs ? 0 : count * Math.round(minDate.getHours() / count)); + } + + if (interval >= TimeUnits.DAY.unitInMs) { // day + minDate.setDate(interval >= TimeUnits.MONTH.unitInMs ? 1 : count * Math.round(minDate.getDate() / count)); + } + + if (interval >= TimeUnits.MONTH.unitInMs) { // month + minDate.setMonth(interval >= TimeUnits.YEAR.unitInMs ? 0 : count * Math.round(minDate.getMonth() + 1 / count) - 1); + minYear = minDate.getYear() + 1900; + } + + if (interval >= TimeUnits.YEAR.unitInMs) { // year + minYear -= minYear % count; + minDate.setYear(minYear - 1900); + } + + // week is a special case that runs outside the hierarchy + if (interval == TimeUnits.WEEK.unitInMs) { + // get start of current week, independent of count + long value = minDate.getDate() - minDate.getDay() + startOfWeek; + minDate.setDate((int) value); + } + + // get tick positions + i = 1; + minYear = minDate.getYear() + 1900; + int minMonth = minDate.getMonth() + 1; + int minDateDate = minDate.getDate(); + long time = minDate.getTime(); + + // iterate and add tick positions at appropriate values + while (time < max) { + if(time > min) + tickPositions.add(new TickPosition(time)); + + // if the interval is years, use Date.UTC to increase years + if (interval == TimeUnits.YEAR.unitInMs) { + time = makeTime(minYear + i * count, 0); + + // if the interval is months, use Date.UTC to increase months + } else if (interval == TimeUnits.MONTH.unitInMs) { + time = makeTime(minYear, minMonth + i * count); + + // if we're using global time, the interval is not fixed as it jumps + // one hour at the DST crossover + } else if (!useUTC && (interval == TimeUnits.DAY.unitInMs || interval == TimeUnits.WEEK.unitInMs)) { + time = makeTime(minYear, minMonth, minDateDate + i * count * (interval == TimeUnits.DAY.unitInMs ? 1 : 7)); + + // else, the interval is fixed and we use simple addition + } else { + time += interval * count; + } + + i++; + } + + // push the last time + if(time < max) + tickPositions.add(new TickPosition(time)); + + // record information on the chosen unit - for dynamic label formatter + // tickPositions.info = extend(normalizedInterval, { + // totalRange: interval * count + // }); + + return tickPositions; + } + + public class NormalizedInterval { + public String unitName; + public long unitRange; + public int count; + + public NormalizedInterval(String unitName, long unitRange, int count) { + this.unitName = unitName; + this.unitRange = unitRange; + this.count = count; + } + } + + public class TickPosition { + public TickPosition(long time) { + this.time = new Date(time); + } + + Date time; + } + + enum TimeUnits { + MILLISECOND(1, new int[] { 1, 2, 5, 10, 20, 25, 50, 100, 200, 500 }), + SECOND(1000, new int[] { 1, 2, 5, 10, 15, 30 }), + MINUTE(60 * 1000, new int[] { 1, 2, 5, 10, 15, 30 }), + HOUR(60 * 60 * 1000, new int[] { 1, 2, 3, 4, 6, 8, 12 }), + DAY(24 * 3600000l, new int[] { 1, 2 }), + WEEK(7 * 24 * 3600000l, new int[] { 1, 2 }), + MONTH(30 * 24 * 3600000l, new int[] { 1, 2, 3, 4, 6 }), + YEAR(31556952000l, new int[] {}); + + int[] allowedMultiples; + + long unitInMs; + + TimeUnits(long unitInMs, int[] allowedMultiples) { + this.unitInMs = unitInMs; + this.allowedMultiples = allowedMultiples; + } + } +}