mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-13 14:12:11 +00:00
Only selected competitors are now loaded for charts.
This commit is contained in:
1 parent
e5b46e81c7
commit
ebda38e09c
4 files changed
+162
-82
No files matched your search
@@ -1,7 +1,7 @@
|
||||
#Thu Dec 08 15:04:04 CET 2011
|
||||
#Mon Dec 12 17:44:33 CET 2011
|
||||
eclipse.preferences.version=1
|
||||
jarsExcludedFromWebInfLib=
|
||||
lastWarOutDir=C\:/Users/d056995/git/git/java/com.sap.sailing.gwt.ui/war
|
||||
lastWarOutDir=C\:/Users/d056866/git/git/java/com.sap.sailing.gwt.ui/war
|
||||
launchConfigExternalUrlPrefix=
|
||||
warSrcDir=war
|
||||
warSrcDirIsOutput=false
|
||||
+135
-78
@@ -1,8 +1,10 @@
|
||||
package com.sap.sailing.gwt.ui.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ca.nanometrics.gflot.client.Axis;
|
||||
import ca.nanometrics.gflot.client.DataPoint;
|
||||
@@ -58,7 +60,7 @@ import com.sap.sailing.server.api.DetailType;
|
||||
import com.sap.sailing.server.api.RaceIdentifier;
|
||||
|
||||
public class ChartsPanel extends FormPanel {
|
||||
private CompetitorInRaceDAO chartData = null;
|
||||
private CompetitorInRaceDAO chartData;
|
||||
private CompetitorsAndTimePointsDAO competitorsAndTimePointsDAO = null;
|
||||
private final SailingServiceAsync sailingService;
|
||||
private DateTimeFormat dateFormat;
|
||||
@@ -69,19 +71,21 @@ public class ChartsPanel extends FormPanel {
|
||||
private DeckPanel chart;
|
||||
private final RaceIdentifier[] races;
|
||||
private int colorCounter = 0;
|
||||
private HashMap<CompetitorDAO, String> competitorColor;
|
||||
private HashMap<Integer, String> idColor;
|
||||
private int selectedRace = 0;
|
||||
private int stepsToLoad = 100;
|
||||
private final StringConstants stringConstants;
|
||||
private HashMap<String, Boolean> competitorVisible = new HashMap<String, Boolean>();
|
||||
private HashMap<CompetitorDAO, Boolean> competitorVisible;
|
||||
private VerticalPanel selectCompetitors;
|
||||
private PlotWithOverview plot;
|
||||
private PlotWithOverviewModel model;
|
||||
private PlotOptions plotOptions;
|
||||
private HashMap<CompetitorDAO, SeriesHandler> competitorSeries;
|
||||
private HashMap<CompetitorDAO, SeriesHandler> competitorMarkPassingSeries;
|
||||
private HashMap<Integer, SeriesHandler> seriesID;
|
||||
private HashMap<CompetitorDAO, Integer> competitorID;
|
||||
private HashMap<Integer, SeriesHandler> markSeriesID;
|
||||
private HashMap<CompetitorDAO, Widget> competitorLabels;
|
||||
private int width = 800, height = 600;
|
||||
private int competitorNr = 0;
|
||||
|
||||
private DetailType dataToShow = DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER;
|
||||
private AbsolutePanel loadingPanel;
|
||||
@@ -90,9 +94,12 @@ public class ChartsPanel extends FormPanel {
|
||||
RaceIdentifier[] races, StringConstants stringConstants, int chartWidth, int chartHeight) {
|
||||
width = chartWidth;
|
||||
height = chartHeight;
|
||||
competitorSeries = new HashMap<CompetitorDAO, SeriesHandler>();
|
||||
competitorMarkPassingSeries = new HashMap<CompetitorDAO, SeriesHandler>();
|
||||
competitorColor = new HashMap<CompetitorDAO, String>();
|
||||
chartData = new CompetitorInRaceDAO();
|
||||
seriesID = new HashMap<Integer, SeriesHandler>();
|
||||
competitorID = new HashMap<CompetitorDAO, Integer>();
|
||||
markSeriesID = new HashMap<Integer, SeriesHandler>();
|
||||
idColor = new HashMap<Integer, String>();
|
||||
competitorVisible = new HashMap<CompetitorDAO, Boolean>();
|
||||
competitorLabels = new HashMap<CompetitorDAO, Widget>();
|
||||
this.sailingService = sailingService;
|
||||
this.races = races;
|
||||
@@ -188,7 +195,8 @@ public class ChartsPanel extends FormPanel {
|
||||
dataToShow = dt;
|
||||
}
|
||||
}
|
||||
|
||||
competitorID.clear();
|
||||
competitorNr = 0;
|
||||
loadData();
|
||||
}
|
||||
});
|
||||
@@ -205,8 +213,6 @@ public class ChartsPanel extends FormPanel {
|
||||
stepsToLoad = Integer.parseInt(txtbSteps.getText());
|
||||
competitorsAndTimePointsDAO = null;
|
||||
loadData();
|
||||
updateTableData();
|
||||
plot.redraw();
|
||||
}
|
||||
});
|
||||
configPanel.add(bttSteps);
|
||||
@@ -218,7 +224,21 @@ public class ChartsPanel extends FormPanel {
|
||||
final Runnable loadData = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ChartsPanel.this.sailingService.getCompetitorRaceData(races[selectedRace], competitorsAndTimePointsDAO,
|
||||
final List<CompetitorDAO> competitorsToLoad = new ArrayList<CompetitorDAO>();
|
||||
for (CompetitorDAO competitor : competitorsAndTimePointsDAO.getCompetitor()){
|
||||
if (isCompetitorVisible(competitor) && competitorID.get(competitor) == null){
|
||||
competitorsToLoad.add(competitor);
|
||||
|
||||
}
|
||||
}
|
||||
final CompetitorsAndTimePointsDAO competitorsAndTimePointsToLoad = new CompetitorsAndTimePointsDAO(stepsToLoad);
|
||||
competitorsAndTimePointsToLoad.setStartTime(competitorsAndTimePointsDAO.getStartTime());
|
||||
competitorsAndTimePointsToLoad.setTimePointOfNewestEvent(competitorsAndTimePointsDAO.getTimePointOfNewestEvent());
|
||||
for (CompetitorDAO competitor : competitorsToLoad){
|
||||
competitorsAndTimePointsToLoad.setMarkPassings(competitor, competitorsAndTimePointsDAO.getMarkPassings(competitor));
|
||||
}
|
||||
competitorsAndTimePointsToLoad.setCompetitor(competitorsToLoad.toArray(new CompetitorDAO[0]));
|
||||
ChartsPanel.this.sailingService.getCompetitorRaceData(races[selectedRace], competitorsAndTimePointsToLoad,
|
||||
dataToShow, new AsyncCallback<CompetitorInRaceDAO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
@@ -228,17 +248,15 @@ public class ChartsPanel extends FormPanel {
|
||||
@Override
|
||||
public void onSuccess(CompetitorInRaceDAO result) {
|
||||
fireEvent(new DataLoadedEvent());
|
||||
chartData = result;
|
||||
while (chart.getWidgetCount() > 1){
|
||||
chart.remove(chart.getWidgetCount()-1);
|
||||
for (CompetitorDAO competitor : competitorsToLoad){
|
||||
chartData.setRaceData(competitor, result.getRaceData(competitor));
|
||||
chartData.setMarkPassingData(competitor, result.getMarkPassings(competitor));
|
||||
}
|
||||
chart.add(createChart());
|
||||
chart.showWidget(1);
|
||||
updateTableData(competitorsAndTimePointsToLoad.getCompetitor());
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
chart.showWidget(0);
|
||||
if (competitorsAndTimePointsDAO != null) {
|
||||
loadData.run();
|
||||
} else {
|
||||
@@ -265,53 +283,74 @@ public class ChartsPanel extends FormPanel {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
setCompetitorVisible(c, cb.getValue());
|
||||
setLegendVisible(c,isCompetitorVisible(c));
|
||||
getCompetitorSeries(c).setVisible(cb.getValue());
|
||||
getCompetitorMarkPassingSeries(c).setVisible(cb.getValue());
|
||||
plot.redraw();
|
||||
if (cb.getValue() == true && chartData.getRaceData(c) == null){
|
||||
setCompetitorVisible(c, cb.getValue());
|
||||
loadData();
|
||||
}
|
||||
else {
|
||||
setCompetitorVisible(c, cb.getValue());
|
||||
if (competitorID.get(c) != null){
|
||||
setLegendVisible(c,cb.getValue());
|
||||
getCompetitorSeries(c).setVisible(cb.getValue());
|
||||
getCompetitorMarkPassingSeries(c).setVisible(cb.getValue());
|
||||
}
|
||||
plot.redraw();
|
||||
}
|
||||
}
|
||||
});
|
||||
selectCompetitors.add(cb);
|
||||
}
|
||||
if (chart.getWidgetCount() == 1){
|
||||
chart.add(createChart());
|
||||
}
|
||||
chart.showWidget(1);
|
||||
loadData.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTableData() {
|
||||
if (model == null || plotOptions == null) {
|
||||
return;
|
||||
}
|
||||
private synchronized void updateTableData(CompetitorDAO[] competitorDAOs) {
|
||||
if (competitorsAndTimePointsDAO != null && chartData != null) {
|
||||
model.clear();
|
||||
for (int i = 0; i < competitorsAndTimePointsDAO.getCompetitor().length; i++) {
|
||||
CompetitorDAO competitor = competitorsAndTimePointsDAO.getCompetitor()[i];
|
||||
for (CompetitorDAO competitor : competitorDAOs) {
|
||||
competitorID.put(competitor, competitorNr++);
|
||||
SeriesHandler compSeries = getCompetitorSeries(competitor);
|
||||
SeriesHandler markSeries = getCompetitorMarkPassingSeries(competitor);
|
||||
compSeries.clear();
|
||||
markSeries.clear();
|
||||
if (isCompetitorVisible(competitor) && chartData.getRaceData(competitor) != null){
|
||||
long[] markPassingTimes = competitorsAndTimePointsDAO.getMarkPassings(competitor);
|
||||
Double[] markPassingValues = chartData.getMarkPassings(competitor);
|
||||
for (int j = 0; j < markPassingTimes.length; j++){
|
||||
if (markPassingValues[j] != null && markPassingTimes != null) {
|
||||
markSeries.add(new DataPoint(markPassingTimes[j],markPassingValues[j]));
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < stepsToLoad; j++) {
|
||||
long time = competitorsAndTimePointsDAO.getTimePoints()[j];
|
||||
if (chartData.getRaceData(competitor)[j] != null) {
|
||||
compSeries.add(new DataPoint(time, chartData.getRaceData(competitor)[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
compSeries.add(new DataPoint(0,0));
|
||||
markSeries.add(new DataPoint(0,0));
|
||||
}
|
||||
setLegendVisible(competitor,isCompetitorVisible(competitor));
|
||||
compSeries.setVisible(isCompetitorVisible(competitor));
|
||||
SeriesHandler markSeries = getCompetitorMarkPassingSeries(competitor);
|
||||
markSeries.setVisible(isCompetitorVisible(competitor));
|
||||
long[] markPassingTimes = competitorsAndTimePointsDAO.getMarkPassings(competitor);
|
||||
Double[] markPassingValues = chartData.getMarkPassings(competitor);
|
||||
for (int j = 0; j < markPassingTimes.length; j++){
|
||||
if (markPassingValues[j] != null) {
|
||||
markSeries.add(new DataPoint(markPassingTimes[j],markPassingValues[j]));
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < stepsToLoad; j++) {
|
||||
long time = competitorsAndTimePointsDAO.getTimePoints()[j];
|
||||
if (chartData.getRaceData(competitor)[j] != null) {
|
||||
compSeries.add(new DataPoint(time, chartData.getRaceData(competitor)[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (plot != null && plot.isAttached()){
|
||||
plot.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
if (plot != null && plot.isAttached()){
|
||||
try {
|
||||
plot.setLinearSelection(1, 2);
|
||||
plot.redraw();
|
||||
}
|
||||
catch (Exception e){
|
||||
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -344,15 +383,11 @@ public class ChartsPanel extends FormPanel {
|
||||
}
|
||||
|
||||
private void setCompetitorVisible(CompetitorDAO competitor, boolean isVisible) {
|
||||
//boolean changed = isCompetitorVisible(competitor) != isVisible;
|
||||
competitorVisible.put(competitor.id, isVisible);
|
||||
/*
|
||||
* if (changed && chart != null){ chart.draw(prepareTableData(), getOptions()); }
|
||||
*/
|
||||
competitorVisible.put(competitor, isVisible);
|
||||
}
|
||||
|
||||
private boolean isCompetitorVisible(CompetitorDAO competitor) {
|
||||
Boolean isVisible = competitorVisible.get(competitor.id);
|
||||
Boolean isVisible = competitorVisible.get(competitor);
|
||||
return (isVisible != null) ? isVisible : false;
|
||||
}
|
||||
|
||||
@@ -378,47 +413,64 @@ public class ChartsPanel extends FormPanel {
|
||||
plotOptions.setGridOptions(new GridOptions().setHoverable(true).setMouseActiveRadius(5).setAutoHighlight(true));
|
||||
|
||||
plotOptions.setSelectionOptions(new SelectionOptions().setDragging(true).setMode("x"));
|
||||
for (CompetitorDAO competitor : competitorsAndTimePointsDAO.getCompetitor()){
|
||||
SeriesHandler series = model.addSeries(competitor.name, getColorForCempetitor(competitor));
|
||||
for (int i = 0; i < competitorsAndTimePointsDAO.getCompetitor().length; i++){
|
||||
SeriesHandler series = model.addSeries(""+i, getColorByID(i));
|
||||
series.setOptions(SeriesType.LINES, new LineSeriesOptions().setLineWidth(2.5).setShow(true));
|
||||
series.setOptions(SeriesType.POINTS, new PointsSeriesOptions().setLineWidth(0).setShow(false));
|
||||
competitorSeries.put(competitor, series);
|
||||
series = model.addSeries(competitor.name + " passed mark", getColorForCempetitor(competitor));
|
||||
series.setVisible(false);
|
||||
seriesID.put(i, series);
|
||||
series = model.addSeries(i + " passed mark", getColorByID(i));
|
||||
series.setOptions(SeriesType.LINES, new LineSeriesOptions().setLineWidth(0).setShow(false));
|
||||
series.setOptions(SeriesType.POINTS, new PointsSeriesOptions().setLineWidth(3).setShow(true));
|
||||
competitorMarkPassingSeries.put(competitor, series);
|
||||
series.setVisible(false);
|
||||
markSeriesID.put(i, series);
|
||||
}
|
||||
updateTableData();
|
||||
plot = new PlotWithOverview(model, plotOptions);
|
||||
// add hover listener
|
||||
plot.addHoverListener(new PlotHoverListener() {
|
||||
public void onPlotHover(Plot plot, PlotPosition position, PlotItem item) {
|
||||
if (item != null) {
|
||||
CompetitorDAO competitor = null;
|
||||
for (Entry<CompetitorDAO, Integer> competitorWithID : competitorID.entrySet()) {
|
||||
if (item != null && item.getSeries().getLabel().contains(""+competitorWithID.getValue())){
|
||||
competitor = competitorWithID.getKey();
|
||||
}
|
||||
}
|
||||
if (item != null && competitor != null) {
|
||||
if (item.getSeries().getLabel().toLowerCase().contains("mark")){
|
||||
selectedPointLabel.setText(item.getSeries().getLabel() + " at " + dateFormat.format(new Date((long) item.getDataPoint().getX())));
|
||||
selectedPointLabel.setText(competitor.name + " passed mark at " + dateFormat.format(new Date((long) item.getDataPoint().getX())));
|
||||
}
|
||||
else {
|
||||
selectedPointLabel.setText(item.getSeries().getLabel() + " at " + dateFormat.format(new Date((long) item.getDataPoint().getX()))
|
||||
selectedPointLabel.setText(competitor.name + " at " + dateFormat.format(new Date((long) item.getDataPoint().getX()))
|
||||
+ ": " + item.getDataPoint().getY() + "[Unit]");
|
||||
}
|
||||
} else {
|
||||
selectedPointLabel.setText("No selection!");
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}, true);
|
||||
plot.addSelectionListener(new SelectionListener() {
|
||||
|
||||
public void selected(double x1, double y1, double x2, double y2) {
|
||||
/*
|
||||
for (CompetitorDAO competitor : competitorsAndTimePointsDAO.getCompetitor()){
|
||||
long[] markPassingTimes = competitorsAndTimePointsDAO.getMarkPassings(competitor);
|
||||
Double[] markPassingValues = chartData.getMarkPassings(competitor);
|
||||
SeriesHandler markSeries = getCompetitorMarkPassingSeries(competitor);
|
||||
markSeries.clear();
|
||||
int visibleMarkPassings = 0;
|
||||
for (int j = 0; j < markPassingTimes.length; j++){
|
||||
if (markPassingValues[j] != null && markPassingTimes[j] > x1 && markPassingTimes[j] < x2) {
|
||||
markSeries.add(new DataPoint(markPassingTimes[j],markPassingValues[j]));
|
||||
visibleMarkPassings++;
|
||||
}
|
||||
}
|
||||
if (visibleMarkPassings == 0){
|
||||
markSeries.setVisible(false);
|
||||
}
|
||||
}*/
|
||||
plot.setLinearSelection(x1, x2);
|
||||
}
|
||||
});
|
||||
plot.addHoverListener(new PlotHoverListener() {
|
||||
|
||||
@Override
|
||||
public void onPlotHover(Plot plot, PlotPosition position, PlotItem item) {
|
||||
|
||||
}
|
||||
}, false);
|
||||
plot.setHeight(height- 60);
|
||||
plot.setWidth(width);
|
||||
plot.setOverviewHeight(60);
|
||||
@@ -426,7 +478,6 @@ public class ChartsPanel extends FormPanel {
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
plot.setLinearSelection(0, stepsToLoad);
|
||||
plot.redraw();
|
||||
}
|
||||
};
|
||||
@@ -435,11 +486,11 @@ public class ChartsPanel extends FormPanel {
|
||||
return panel;
|
||||
}
|
||||
|
||||
private String getColorForCempetitor(CompetitorDAO competitor){
|
||||
String color = competitorColor.get(competitor);
|
||||
private String getColorByID(int id){
|
||||
String color = idColor.get(id);
|
||||
if (color == null || color.isEmpty()){
|
||||
color = createHexColor(colorCounter++);
|
||||
competitorColor.put(competitor, color);
|
||||
idColor.put(id, color);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
@@ -473,17 +524,17 @@ public class ChartsPanel extends FormPanel {
|
||||
}
|
||||
|
||||
private SeriesHandler getCompetitorSeries(CompetitorDAO competitor){
|
||||
return competitorSeries.get(competitor);
|
||||
return seriesID.get(competitorID.get(competitor));
|
||||
}
|
||||
|
||||
private SeriesHandler getCompetitorMarkPassingSeries(CompetitorDAO competitor){
|
||||
return competitorMarkPassingSeries.get(competitor);
|
||||
return markSeriesID.get(competitorID.get(competitor));
|
||||
}
|
||||
|
||||
private void setLegendVisible(CompetitorDAO competitor, Boolean visible){
|
||||
Widget label = competitorLabels.get(competitor);
|
||||
if (label == null){
|
||||
label = createCompetitorLabel(competitor.name, getColorForCempetitor(competitor));
|
||||
label = createCompetitorLabel(competitor.name, getColorByID(competitorID.get(competitor)));
|
||||
competitorLabels.put(competitor, label);
|
||||
legendPanel.add(label);
|
||||
}
|
||||
@@ -504,4 +555,10 @@ public class ChartsPanel extends FormPanel {
|
||||
competitorLabel.add(legendLabel);
|
||||
return competitorLabel;
|
||||
}
|
||||
|
||||
public void resize(int width, int height){
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.setSize(width + "px", height + "px");
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -5,6 +5,8 @@ import java.util.List;
|
||||
import com.google.gwt.dom.client.EventTarget;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.event.logical.shared.ResizeEvent;
|
||||
import com.google.gwt.event.logical.shared.ResizeHandler;
|
||||
import com.google.gwt.user.client.Element;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
@@ -22,7 +24,7 @@ public class CompareCompetitorsChartDialog extends DialogBox {
|
||||
|
||||
public CompareCompetitorsChartDialog(SailingServiceAsync sailingService, List<CompetitorDAO> competitors, RaceIdentifier[] races, StringConstants stringConstants){
|
||||
super(false);
|
||||
ChartsPanel ccp = new ChartsPanel(sailingService, competitors, races, stringConstants, (int) (Window.getClientWidth()-300), (int) (Window.getClientHeight()-200));
|
||||
final ChartsPanel ccp = new ChartsPanel(sailingService, competitors, races, stringConstants, (int) (Window.getClientWidth()-250), (int) (Window.getClientHeight()-90));
|
||||
ccp.addDataLoadedHandler(new DataLoadedHandler() {
|
||||
|
||||
@Override
|
||||
@@ -64,7 +66,13 @@ public class CompareCompetitorsChartDialog extends DialogBox {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
Window.addResizeHandler(new ResizeHandler() {
|
||||
|
||||
@Override
|
||||
public void onResize(ResizeEvent event) {
|
||||
ccp.resize((int) (Window.getClientWidth()-250), (int) (Window.getClientHeight()-90));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addCloseHandler(ClickHandler handler) {
|
||||
|
||||
+15
@@ -206,6 +206,21 @@ public interface SailingServiceAsync {
|
||||
|
||||
void getCountryCodes(AsyncCallback<String[]> callback);
|
||||
|
||||
/**
|
||||
* This method computes the in {@code dataType} selected data for the in {@code race} specified race
|
||||
* for all competitors returned by {@link CompetitorsAndTimePointsDAO#getCompetitor()} at the timepoints
|
||||
* returned by {@link CompetitorsAndTimePointsDAO#getTimePoints()}.
|
||||
* The returned {@link CompetitorInRaceDAO} contains the values for the timepoints as well as the values for the markpassings.
|
||||
*
|
||||
* @see DetailType
|
||||
*
|
||||
* @throws NullPointerException Thrown if any of the parameters is null.
|
||||
*
|
||||
* @param race
|
||||
* @param competitorsAndTimePointsDAO An object that contains the competitors and timepoints.
|
||||
* @param dataType The type of data that should be computed (eg {@link DetailType#WINDWARD_DISTANCE_TO_OVERALL_LEADER}).
|
||||
* @param callback An AsyncCallback that returns the computed data as a parameter in the {@link AsyncCallback#onSuccess(Object)} method.
|
||||
*/
|
||||
void getCompetitorRaceData(RaceIdentifier race,
|
||||
CompetitorsAndTimePointsDAO competitorsAndTimePointsDAO,
|
||||
DetailType dataType, AsyncCallback<CompetitorInRaceDAO> callback);
|
||||
|
||||
Reference in new issue
Block a user