mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-17 01:31:01 +00:00
Merge branch 'master' into racecommittee_integrated
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
This commit is contained in:
commit
b05d0b9198
66 files changed
+1078
-403
No files matched your search
+2
-1
@@ -15,7 +15,8 @@ public enum DetailType implements Serializable {
|
||||
DISTANCE_TRAVELED(0, ASCENDING), AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_RANK(0, ASCENDING),
|
||||
RANK_GAIN(0, DESCENDING), NUMBER_OF_MANEUVERS(0, ASCENDING), CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING),
|
||||
ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS(1, ASCENDING), VELOCITY_MADE_GOOD_IN_KNOTS(2, DESCENDING),
|
||||
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
|
||||
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), GAP_CHANGE_SINCE_LEG_START_IN_SECONDS(0, ASCENDING),
|
||||
SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
|
||||
AVERAGE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), RACE_DISTANCE_TRAVELED(0, ASCENDING),
|
||||
RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_GAP_TO_LEADER_IN_SECONDS(0, ASCENDING),
|
||||
RACE_DISTANCE_TO_LEADER_IN_METERS(0, ASCENDING), RACE_AVERAGE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING),
|
||||
|
||||
+28
-16
@@ -10,43 +10,55 @@ package com.sap.sailing.domain.common;
|
||||
public enum MaxPointsReason {
|
||||
|
||||
/** The competitor finished the race properly */
|
||||
NONE(true),
|
||||
NONE(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
|
||||
/** Did Not Start */
|
||||
DNS(true),
|
||||
DNS(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Did Not Finish */
|
||||
DNF(true),
|
||||
DNF(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** DiSQualified */
|
||||
DSQ(true),
|
||||
DSQ(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** On Course Side (jumped the gun) */
|
||||
OCS(true),
|
||||
OCS(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Disqualified, non-discardable */
|
||||
DND(false),
|
||||
DND(/* discardable */ false, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** 20 % penalty under rule 30.2 */
|
||||
ZFP(true),
|
||||
ZFP(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
|
||||
/** Took a Scoring penalty under rule 44.3 (a) */
|
||||
SCP(true),
|
||||
SCP(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
|
||||
/** Disqualification not excludable under rule 90.3 (b) */
|
||||
DNE(false),
|
||||
DNE(/* discardable */ false, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Disqualification for gross misconduct not excludable under rule 90.3 (b) */
|
||||
DGM(false),
|
||||
DGM(/* discardable */ false, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Redress given */
|
||||
RDG(true),
|
||||
RDG(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
|
||||
/** Black Flag Disqualified */
|
||||
BFD(true),
|
||||
BFD(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Did Not Compete */
|
||||
DNC(true),
|
||||
DNC(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Retired After Finishing */
|
||||
RAF(true),
|
||||
RAF(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
|
||||
/** Discretionary Penalty Imposed by the race committee */
|
||||
DPI(true);
|
||||
DPI(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false);
|
||||
|
||||
private final boolean discardable;
|
||||
|
||||
private final boolean advanceCompetitorsTrackedWorse;
|
||||
|
||||
private MaxPointsReason(boolean discardable) {
|
||||
private MaxPointsReason(boolean discardable, boolean advanceCompetitorsTrackedWorse) {
|
||||
this.discardable = discardable;
|
||||
this.advanceCompetitorsTrackedWorse = advanceCompetitorsTrackedWorse;
|
||||
}
|
||||
|
||||
public boolean isDiscardable() {
|
||||
return discardable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Most "max points reasons" are a penalty. If the competitor has finished the race and by the penalty is ranked to the "bottom,"
|
||||
* those competitors tracked worse usually will advance by one rank. However, "max points reasons" such as a redress given (RDG)
|
||||
* are different. In this case, only the points awarded to the competitor change without any effect on other competitors.
|
||||
*/
|
||||
public boolean isAdvanceCompetitorsTrackedWorse() {
|
||||
return advanceCompetitorsTrackedWorse;
|
||||
}
|
||||
}
|
||||
+1
@@ -90,6 +90,7 @@ public class MarkPassingReceiver extends AbstractReceiverWithQueue<RaceCompetito
|
||||
markPassings.add(passing);
|
||||
}
|
||||
}
|
||||
logger.fine("Received mark passings in race "+trackedRace.getRace().getName()+": "+markPassings);
|
||||
if (getSimulator() != null) {
|
||||
getSimulator().delayMarkPassings(getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor()), markPassings);
|
||||
} else {
|
||||
|
||||
+4
-2
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
|
||||
/**
|
||||
* A leaderboard has a scoring scheme that decides how race ranks map to scores, how penalties are to be scored,
|
||||
@@ -67,7 +68,8 @@ public interface ScoringScheme extends Serializable {
|
||||
* @param competitor1Scores scores of the first competitor, in the order of race columns in the leaderboard
|
||||
* @param competitor2Scores scores of the second competitor, in the order of race columns in the leaderboard
|
||||
*/
|
||||
int compareByBetterScore(List<Double> competitor1Scores, List<Double> competitor2Scores, boolean nullScoresAreBetter);
|
||||
int compareByBetterScore(List<Pair<RaceColumn, Double>> competitor1Scores,
|
||||
List<Pair<RaceColumn, Double>> competitor2Scores, boolean nullScoresAreBetter);
|
||||
|
||||
/**
|
||||
* In case two competitors scored in different numbers of races, this scoring scheme decides whether this
|
||||
@@ -81,7 +83,7 @@ public interface ScoringScheme extends Serializable {
|
||||
/**
|
||||
* Usually, when all other sorting criteria end up in a tie, the last race sailed is used to decide.
|
||||
*/
|
||||
int compareByLastRace(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter);
|
||||
int compareByLastRace(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter);
|
||||
|
||||
/**
|
||||
* Under certain circumstances, a scoring scheme may decide that the scores of a column are not (yet) to be used
|
||||
|
||||
+5
-3
@@ -107,8 +107,9 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
|
||||
}
|
||||
|
||||
/**
|
||||
* Per competitor disqualified ({@link ScoreCorrection} has a {@link MaxPointsReason} for the competitor), all
|
||||
* competitors ranked worse by the tracking system need to have their rank corrected by one.
|
||||
* Per competitor disqualified ({@link ScoreCorrection} has a {@link MaxPointsReason} for the competitor that has
|
||||
* <code>{@link MaxPointsReason#isAdvanceCompetitorsTrackedWorse()}==true</code>), all competitors ranked worse by
|
||||
* the tracking system need to have their rank corrected by one.
|
||||
*
|
||||
* @param trackedRace
|
||||
* the race to which the rank refers; look for disqualifications / max points reasons in this column
|
||||
@@ -131,7 +132,8 @@ public abstract class AbstractLeaderboardImpl extends AbstractSimpleLeaderboardI
|
||||
while (betterCompetitorRank < rank && ci.hasNext()) {
|
||||
Competitor betterTrackedCompetitor = ci.next();
|
||||
MaxPointsReason maxPointsReasonForBetterCompetitor = getScoreCorrection().getMaxPointsReason(betterTrackedCompetitor, raceColumn);
|
||||
if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE) {
|
||||
if (maxPointsReasonForBetterCompetitor != null && maxPointsReasonForBetterCompetitor != MaxPointsReason.NONE &&
|
||||
maxPointsReasonForBetterCompetitor.isAdvanceCompetitorsTrackedWorse()) {
|
||||
correctedRank--;
|
||||
}
|
||||
betterCompetitorRank++;
|
||||
|
||||
+16
-8
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -120,16 +121,22 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
* Assuming both competitors scored in the same number of races, compares the sorted scores.
|
||||
*/
|
||||
@Override
|
||||
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
|
||||
public int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
|
||||
assert o1Scores.size() == o2Scores.size();
|
||||
Comparator<Double> scoreComparator = getScoreComparator(nullScoresAreBetter);
|
||||
List<Double> o1ScoresCopy = new ArrayList<Double>(o1Scores);
|
||||
List<Double> o2ScoresCopy = new ArrayList<Double>(o2Scores);
|
||||
final Comparator<Double> pureScoreComparator = getScoreComparator(nullScoresAreBetter);
|
||||
Comparator<Pair<RaceColumn, Double>> scoreComparator = new Comparator<Pair<RaceColumn, Double>>() {
|
||||
@Override
|
||||
public int compare(Pair<RaceColumn, Double> o1, Pair<RaceColumn, Double> o2) {
|
||||
return pureScoreComparator.compare(o1.getB(), o2.getB());
|
||||
}
|
||||
};
|
||||
List<Pair<RaceColumn, Double>> o1ScoresCopy = new ArrayList<Pair<RaceColumn, Double>>(o1Scores);
|
||||
List<Pair<RaceColumn, Double>> o2ScoresCopy = new ArrayList<Pair<RaceColumn, Double>>(o2Scores);
|
||||
Collections.sort(o1ScoresCopy, scoreComparator);
|
||||
Collections.sort(o2ScoresCopy, scoreComparator);
|
||||
// now both lists are sorted from best to worst score
|
||||
Iterator<Double> o1Iter = o1ScoresCopy.iterator();
|
||||
Iterator<Double> o2Iter = o2ScoresCopy.iterator();
|
||||
Iterator<Pair<RaceColumn, Double>> o1Iter = o1ScoresCopy.iterator();
|
||||
Iterator<Pair<RaceColumn, Double>> o2Iter = o2ScoresCopy.iterator();
|
||||
int result = 0;
|
||||
while (result == 0 && o1Iter.hasNext() && o2Iter.hasNext()) {
|
||||
result = scoreComparator.compare(o1Iter.next(), o2Iter.next());
|
||||
@@ -147,10 +154,11 @@ public abstract class AbstractScoringSchemeImpl implements ScoringScheme {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareByLastRace(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
|
||||
public int compareByLastRace(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
|
||||
int result = 0;
|
||||
if (!o1Scores.isEmpty() && !o2Scores.isEmpty()) {
|
||||
result = getScoreComparator(nullScoresAreBetter).compare(o1Scores.get(o1Scores.size()-1), o2Scores.get(o2Scores.size()-1));
|
||||
result = getScoreComparator(nullScoresAreBetter).compare(o1Scores.get(o1Scores.size()-1).getB(),
|
||||
o2Scores.get(o2Scores.size()-1).getB());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+7
-6
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
|
||||
/**
|
||||
* A variant of the {@link HighPoint} scoring scheme which breaks ties differently and which assigns a score of 10 to
|
||||
@@ -42,13 +43,13 @@ public class HighPointExtremeSailingSeriesOverall extends HighPoint {
|
||||
* Implements rule 13.5 of the Extreme Sailing Series notice of race as of August 2012.
|
||||
*/
|
||||
@Override
|
||||
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
|
||||
public int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
|
||||
assert o1Scores.size() == o2Scores.size();
|
||||
int o1Wins = getWins(o1Scores);
|
||||
int o2Wins = getWins(o2Scores);
|
||||
int result = o1Wins - o2Wins;
|
||||
int result = o2Wins - o1Wins;
|
||||
if (result == 0 && o1Scores.size() >= 1 && o2Scores.size() >= 1) {
|
||||
result = o1Scores.get(o1Scores.size()-1).compareTo(o2Scores.get(o2Scores.size()-1));
|
||||
result = o1Scores.get(o1Scores.size()-1).getB().compareTo(o2Scores.get(o2Scores.size()-1).getB());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -57,10 +58,10 @@ public class HighPointExtremeSailingSeriesOverall extends HighPoint {
|
||||
* Counts a competitor's wins by comparing the scores to {@link #MAX_POINTS} which is the score attributed to a race
|
||||
* won
|
||||
*/
|
||||
private int getWins(List<Double> scores) {
|
||||
private int getWins(List<Pair<RaceColumn, Double>> scores) {
|
||||
int wins = 0;
|
||||
for (Double score : scores) {
|
||||
if (score == MAX_POINTS) {
|
||||
for (Pair<RaceColumn, Double> score : scores) {
|
||||
if (score.getB() == MAX_POINTS * score.getA().getFactor()) {
|
||||
wins++;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,7 +2,9 @@ package com.sap.sailing.domain.leaderboard.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
|
||||
/**
|
||||
* In this variant of the {@link HighPoint} scoring scheme, scoring by "better race" (sometimes referred to as a
|
||||
@@ -16,7 +18,7 @@ public class HighPointLastBreaksTie extends HighPoint {
|
||||
private static final long serialVersionUID = -5338636946886101669L;
|
||||
|
||||
@Override
|
||||
public int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores, boolean nullScoresAreBetter) {
|
||||
public int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores, boolean nullScoresAreBetter) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -93,8 +93,8 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
|
||||
@Override
|
||||
public int compare(Competitor o1, Competitor o2) {
|
||||
List<Double> o1Scores = new ArrayList<Double>();
|
||||
List<Double> o2Scores = new ArrayList<Double>();
|
||||
List<Pair<RaceColumn, Double>> o1Scores = new ArrayList<Pair<RaceColumn, Double>>();
|
||||
List<Pair<RaceColumn, Double>> o2Scores = new ArrayList<Pair<RaceColumn, Double>>();
|
||||
double o1ScoreSum = getLeaderboard().getCarriedPoints(o1);
|
||||
double o2ScoreSum = getLeaderboard().getCarriedPoints(o2);
|
||||
Double o1MedalRaceScore = 0.0;
|
||||
@@ -104,12 +104,12 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
int preemptiveColumnResult = 0;
|
||||
final Double o1Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o1, raceColumn));
|
||||
if (o1Score != null) {
|
||||
o1Scores.add(o1Score);
|
||||
o1Scores.add(new Pair<RaceColumn, Double>(raceColumn, o1Score));
|
||||
o1ScoreSum += o1Score;
|
||||
}
|
||||
final Double o2Score = totalPointsCache.get(new Pair<Competitor, RaceColumn>(o2, raceColumn));
|
||||
if (o2Score != null) {
|
||||
o2Scores.add(o2Score);
|
||||
o2Scores.add(new Pair<RaceColumn, Double>(raceColumn, o2Score));
|
||||
o2ScoreSum += o2Score;
|
||||
}
|
||||
if (raceColumn.isMedalRace()) {
|
||||
@@ -237,7 +237,7 @@ public class LeaderboardTotalRankComparator implements Comparator<Competitor> {
|
||||
*
|
||||
* @see ScoringScheme#compareByBetterScore(List, List, boolean)
|
||||
*/
|
||||
protected int compareByBetterScore(List<Double> o1Scores, List<Double> o2Scores) {
|
||||
protected int compareByBetterScore(List<Pair<RaceColumn, Double>> o1Scores, List<Pair<RaceColumn, Double>> o2Scores) {
|
||||
return scoringScheme.compareByBetterScore(o1Scores, o2Scores, nullScoresAreBetter);
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
|
||||
public interface MarkPassingManeuver extends Maneuver {
|
||||
Waypoint getWaypointPassed();
|
||||
|
||||
Tack getSide();
|
||||
}
|
||||
+10
-6
@@ -78,11 +78,14 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
|
||||
/**
|
||||
* Infers the maneuvers of the competitor up to <code>timePoint</code> on this leg. If the competitor hasn't started
|
||||
* the leg at the time point specified, <code>null</code> is returned. Otherwise, the list will be valid. If the
|
||||
* time point is after the competitor has finished this leg, all of the competitor's maneuvers during this leg will
|
||||
* be reported in chronological order. The list may be empty if no maneuvers happened between the point in time when
|
||||
* the competitor started the leg and <code>timePoint</code>.
|
||||
* @param waitForLatest TODO
|
||||
* the leg at the time point specified, an empty list is returned. If the time point is after the competitor has
|
||||
* finished this leg, all of the competitor's maneuvers during this leg will be reported in chronological order. The
|
||||
* list may be empty if no maneuvers happened between the point in time when the competitor started the leg and
|
||||
* <code>timePoint</code>.<p>
|
||||
*
|
||||
* Note that the mark passing maneuver at leg start and finish are not guaranteed to be part of this leg's maneuvers. They
|
||||
* may be part of the respective adjacent leg, depending on the maneuver's time point which may be slightly before, at, or
|
||||
* after the corresponding mark passing event.
|
||||
*/
|
||||
List<Maneuver> getManeuvers(TimePoint timePoint, boolean waitForLatest) throws NoWindException;
|
||||
|
||||
@@ -190,7 +193,8 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* as defined by <code>timePointBeforeManeuver</code> is extrapolated until <code>timePointAfterManeuver</code>,
|
||||
* and the resulting extrapolated position's "windward distance" is computed to the competitor's actual position
|
||||
* at that time. This distance is returned as the result of this method.
|
||||
* @param maneuverTimePoint TODO
|
||||
*/
|
||||
Distance getManeuverLoss(TimePoint timePointBeforeManeuver, TimePoint maneuverTimePoint, TimePoint timePointAfterManeuver) throws NoWindException;
|
||||
|
||||
TrackedLeg getTrackedLeg();
|
||||
}
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
|
||||
public class MarkPassingManeuverImpl extends ManeuverImpl implements MarkPassingManeuver {
|
||||
private static final long serialVersionUID = 8935348908557191614L;
|
||||
private final Waypoint waypointPassed;
|
||||
private final Tack side;
|
||||
|
||||
public MarkPassingManeuverImpl(ManeuverType type, Tack newTack, Position position, TimePoint timePoint,
|
||||
SpeedWithBearing speedWithBearingBefore, SpeedWithBearing speedWithBearingAfter,
|
||||
double directionChangeInDegrees, Distance maneuverLoss, Waypoint waypointPassed, Tack side) {
|
||||
super(type, newTack, position, timePoint, speedWithBearingBefore, speedWithBearingAfter,
|
||||
directionChangeInDegrees, maneuverLoss);
|
||||
this.waypointPassed = waypointPassed;
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Waypoint getWaypointPassed() {
|
||||
return waypointPassed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getSide() {
|
||||
return side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder(super.toString());
|
||||
result.append(", passed waypoint "+getWaypointPassed()+" to "+getSide().name());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -46,7 +46,8 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
this.competitor = competitor;
|
||||
}
|
||||
|
||||
protected TrackedLegImpl getTrackedLeg() {
|
||||
@Override
|
||||
public TrackedLegImpl getTrackedLeg() {
|
||||
return trackedLeg;
|
||||
}
|
||||
|
||||
@@ -430,7 +431,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
return getGapToLeaderInSeconds(timePoint, new LeaderGetter() {
|
||||
@Override
|
||||
public Competitor getLeader() {
|
||||
return getTrackedLeg().getLeader(timePoint);
|
||||
return getTrackedLeg().getLeader(hasFinishedLeg(timePoint) ? getFinishTime() : timePoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+14
-3
@@ -1915,12 +1915,16 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
// the TrackedLegOfCompetitor variables may be null, e.g., in case the time points are before or after the race
|
||||
TrackedLegOfCompetitor legBeforeManeuver = getTrackedLeg(competitor, timePointBeforeManeuver);
|
||||
TrackedLegOfCompetitor legAfterManeuver = getTrackedLeg(competitor, timePointAfterManeuver);
|
||||
Waypoint waypointPassed = null; // set for MARK_PASSING maneuvers only
|
||||
Tack sideToWhichWaypointWasPassed = null; // set for MARK_PASSING maneuvers only
|
||||
// check for mask passing first; a tacking / jibe-setting mark rounding thus takes precedence over being detected as a penalty circle
|
||||
if (legBeforeManeuver != legAfterManeuver
|
||||
// a maneuver at the start line is not to be considered a MARK_PASSING maneuver; show a tack as a tack
|
||||
&& legAfterManeuver != null
|
||||
&& legAfterManeuver.getLeg().getFrom() != getRace().getCourse().getFirstWaypoint()) {
|
||||
maneuverType = ManeuverType.MARK_PASSING;
|
||||
waypointPassed = legAfterManeuver.getLeg().getFrom();
|
||||
sideToWhichWaypointWasPassed = totalCourseChangeInDegrees < 0 ? Tack.PORT : Tack.STARBOARD;
|
||||
} else if (Math.abs(totalCourseChangeInDegrees) > PENALTY_CIRCLE_DEGREES_THRESHOLD) {
|
||||
maneuverType = ManeuverType.PENALTY_CIRCLE;
|
||||
if (legBeforeManeuver != null) {
|
||||
@@ -1973,9 +1977,16 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
: ManeuverType.BEAR_AWAY;
|
||||
}
|
||||
}
|
||||
Maneuver maneuver = new ManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
|
||||
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
|
||||
totalCourseChangeInDegrees, maneuverLoss);
|
||||
final Maneuver maneuver;
|
||||
if (maneuverType == ManeuverType.MARK_PASSING) {
|
||||
maneuver = new MarkPassingManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
|
||||
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
|
||||
totalCourseChangeInDegrees, maneuverLoss, waypointPassed, sideToWhichWaypointWasPassed);
|
||||
} else {
|
||||
maneuver = new ManeuverImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverTimePoint,
|
||||
speedWithBearingOnApproximationAtBeginning, speedWithBearingOnApproximationAtEnd,
|
||||
totalCourseChangeInDegrees, maneuverLoss);
|
||||
}
|
||||
return maneuver;
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -18,8 +18,9 @@ public class LeaderboardPanelMock extends LeaderboardPanel {
|
||||
String leaderboardName, ErrorReporter errorReporter,
|
||||
StringMessages stringConstants) {
|
||||
super(sailingService, null, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */false), new CompetitorSelectionModel(
|
||||
/* hasMultiSelection */true), leaderboardName, null, errorReporter, stringConstants, null, /* showRaceDetails */ true);
|
||||
/* racesToShow */null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false,
|
||||
/* showMetaLeaderboardsOnSamePage */ false), new CompetitorSelectionModel(
|
||||
/* hasMultiSelection */true), leaderboardName, errorReporter, stringConstants, null, /* showRaceDetails */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -181,9 +181,19 @@ body ::-webkit-scrollbar-track {
|
||||
font-family: 'UbuntuMedium', Arial, Verdana, sans-serif;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
margin: 13px 0 0 0;
|
||||
margin-top: 13px;
|
||||
}
|
||||
|
||||
.titleLabelRight {
|
||||
text-align: right;
|
||||
font-family: 'UbuntuMedium', Arial, Verdana, sans-serif;
|
||||
font-size: 18px;
|
||||
color: #fff;
|
||||
margin-top: 13px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
|
||||
.subTitleLabelWrapper {
|
||||
position: absolute;
|
||||
min-width: 200px;
|
||||
@@ -444,7 +454,8 @@ body ::-webkit-scrollbar-track {
|
||||
|
||||
.sapLogo {
|
||||
float: left;
|
||||
margin: 13px 0 0 12px;
|
||||
margin-top: 13px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.sailingAnalyticsLabelPanel {
|
||||
@@ -546,7 +557,6 @@ body ::-webkit-scrollbar-track {
|
||||
cursor: default;
|
||||
color: #888;
|
||||
background: #F1F1F1;
|
||||
border: 1px solid #919090;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,11 @@ h1 {
|
||||
table {
|
||||
border-collapse: separate; }
|
||||
|
||||
.leaderboardMargin {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.leaderboardContent {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.leaderboardContent td, .leaderboardContent th {
|
||||
@@ -119,6 +122,12 @@ body, table td, select, button {
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.leaderboardHeading {
|
||||
font-size: 16px;
|
||||
font-family: 'UbuntuRegular', Arial, Verdana, sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.gwt-DialogBox {
|
||||
z-index: 20;
|
||||
}
|
||||
+8
-3
@@ -19,8 +19,7 @@ import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.RootPanel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
|
||||
public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter {
|
||||
|
||||
public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter, WindowSizeDetector {
|
||||
private DialogBox errorDialogBox;
|
||||
private HTML serverResponseLabel;
|
||||
private Button dialogCloseButton;
|
||||
@@ -75,9 +74,15 @@ public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter {
|
||||
reportError(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSmallWidth() {
|
||||
int width = Window.getClientWidth();
|
||||
return width <= 720;
|
||||
}
|
||||
|
||||
public void createErrorPage(String message) {
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages, this);
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
RootPanel.get().add(logoAndTitlePanel);
|
||||
RootPanel.get().add(new Label(message));
|
||||
|
||||
+4
@@ -25,6 +25,10 @@ public class DetailTypeFormatter {
|
||||
return stringMessages.velocityMadeGoodInKnots();
|
||||
case GAP_TO_LEADER_IN_SECONDS:
|
||||
return stringMessages.gapToLeaderInSeconds();
|
||||
case GAP_CHANGE_SINCE_LEG_START_IN_SECONDS:
|
||||
return stringMessages.gapChangeSinceLegStartInSeconds();
|
||||
case SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED:
|
||||
return stringMessages.sideToWhichMarkAtLegStartWasRounded();
|
||||
case WINDWARD_DISTANCE_TO_GO_IN_METERS:
|
||||
return stringMessages.windwardDistanceToGoInMeters();
|
||||
case RACE_DISTANCE_TRAVELED:
|
||||
|
||||
+61
-27
@@ -4,47 +4,72 @@ import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
|
||||
public class LogoAndTitlePanel extends FlowPanel {
|
||||
protected Label titleLabel;
|
||||
protected Label subTitleLabel;
|
||||
public class LogoAndTitlePanel extends FlowPanel implements RequiresResize {
|
||||
private final String title;
|
||||
private final String subtitle;
|
||||
private Label titleLabel;
|
||||
private Label subTitleLabel;
|
||||
private final Anchor sapLogo;
|
||||
private boolean lastIsSmallWidth;
|
||||
private final WindowSizeDetector windowSizeDetector;
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
public LogoAndTitlePanel(StringMessages stringConstants) {
|
||||
this(null, null, stringConstants);
|
||||
public LogoAndTitlePanel(StringMessages stringConstants, WindowSizeDetector windowSizeDetector) {
|
||||
this(null, null, stringConstants, windowSizeDetector);
|
||||
}
|
||||
|
||||
public LogoAndTitlePanel(String title, StringMessages stringConstants) {
|
||||
this(title, null, stringConstants);
|
||||
public LogoAndTitlePanel(String title, StringMessages stringConstants, WindowSizeDetector windowSizeDetector) {
|
||||
this(title, null, stringConstants, windowSizeDetector);
|
||||
}
|
||||
|
||||
public LogoAndTitlePanel(String title, String subTitle, StringMessages stringConstants) {
|
||||
Anchor sapLogo = new Anchor(new SafeHtmlBuilder().appendHtmlConstant(
|
||||
public LogoAndTitlePanel(String title, String subtitle, StringMessages stringMessages, WindowSizeDetector windowSizeDetector) {
|
||||
this.title = title;
|
||||
this.subtitle = subtitle;
|
||||
this.stringMessages = stringMessages;
|
||||
this.windowSizeDetector = windowSizeDetector;
|
||||
lastIsSmallWidth = windowSizeDetector.isSmallWidth();
|
||||
sapLogo = new Anchor(new SafeHtmlBuilder().appendHtmlConstant(
|
||||
"<img class=\"linkNoBorder\" src=\"/gwt/images/sap_66_transparent.png\"/>").toSafeHtml());
|
||||
sapLogo.setHref("http://www.sap.com");
|
||||
sapLogo.addStyleName("sapLogo");
|
||||
this.add(sapLogo);
|
||||
|
||||
FlowPanel sailingAnalyticsLabelPanel = new FlowPanel();
|
||||
Label sailingAnalyticsLabel = new Label(stringConstants.sapSailingAnalytics());
|
||||
sailingAnalyticsLabelPanel.add(sailingAnalyticsLabel);
|
||||
sailingAnalyticsLabelPanel.addStyleName("sailingAnalyticsLabelPanel");
|
||||
sailingAnalyticsLabel.addStyleName("sailingAnalyticsLabel boldLabel");
|
||||
this.add(sailingAnalyticsLabelPanel);
|
||||
|
||||
if (title != null) {
|
||||
FlowPanel subTitleLabelWrapper = new FlowPanel();
|
||||
subTitleLabelWrapper.addStyleName("titleLabelWrapper");
|
||||
renderUI();
|
||||
}
|
||||
|
||||
private void renderUI() {
|
||||
for (int i=getWidgetCount()-1; i>=0; i--) {
|
||||
if (getWidget(i) != sapLogo) {
|
||||
remove(i);
|
||||
}
|
||||
}
|
||||
if (!lastIsSmallWidth) {
|
||||
FlowPanel sailingAnalyticsLabelPanel = new FlowPanel();
|
||||
Label sailingAnalyticsLabel = new Label(stringMessages.sapSailingAnalytics());
|
||||
sailingAnalyticsLabelPanel.add(sailingAnalyticsLabel);
|
||||
sailingAnalyticsLabelPanel.addStyleName("sailingAnalyticsLabelPanel");
|
||||
sailingAnalyticsLabel.addStyleName("sailingAnalyticsLabel boldLabel");
|
||||
this.add(sailingAnalyticsLabelPanel);
|
||||
}
|
||||
if (!lastIsSmallWidth && title != null) {
|
||||
FlowPanel titleLabelWrapper = new FlowPanel();
|
||||
titleLabelWrapper.addStyleName("titleLabelWrapper");
|
||||
titleLabel = new Label(title);
|
||||
titleLabel.addStyleName("titleLabel");
|
||||
subTitleLabelWrapper.add(titleLabel);
|
||||
this.add(subTitleLabelWrapper);
|
||||
titleLabelWrapper.add(titleLabel);
|
||||
this.add(titleLabelWrapper);
|
||||
}
|
||||
|
||||
if (subTitle != null) {
|
||||
if (subtitle != null) {
|
||||
subTitleLabel = new Label(subtitle);
|
||||
FlowPanel subTitleLabelWrapper = new FlowPanel();
|
||||
subTitleLabelWrapper.addStyleName("subTitleLabelWrapper");
|
||||
subTitleLabel = new Label(subTitle);
|
||||
subTitleLabel.addStyleName("subTitleLabel");
|
||||
if (lastIsSmallWidth) {
|
||||
subTitleLabelWrapper.addStyleName("titleLabelWrapper");
|
||||
subTitleLabel.addStyleName("titleLabelRight");
|
||||
} else {
|
||||
subTitleLabelWrapper.addStyleName("subTitleLabelWrapper");
|
||||
subTitleLabel.addStyleName("subTitleLabel");
|
||||
}
|
||||
subTitleLabelWrapper.add(subTitleLabel);
|
||||
this.add(subTitleLabelWrapper);
|
||||
}
|
||||
@@ -68,5 +93,14 @@ public class LogoAndTitlePanel extends FlowPanel {
|
||||
if(subTitleLabel != null) {
|
||||
subTitleLabel.setText(subTitle);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResize() {
|
||||
if (windowSizeDetector.isSmallWidth() != lastIsSmallWidth) {
|
||||
lastIsSmallWidth = windowSizeDetector.isSmallWidth();
|
||||
renderUI();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+8
-7
@@ -18,7 +18,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
|
||||
private boolean autoAdjustPlayMode;
|
||||
private RaceTimesInfoDTO lastRaceTimesInfo;
|
||||
|
||||
public RaceTimePanel(Timer timer, TimeRangeProvider timeRangeProvider, StringMessages stringMessages, RaceTimesInfoProvider raceTimesInfoProvider) {
|
||||
public RaceTimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages, RaceTimesInfoProvider raceTimesInfoProvider) {
|
||||
super(timer, timeRangeProvider, stringMessages);
|
||||
this.raceTimesInfoProvider = raceTimesInfoProvider;
|
||||
selectedRace = null;
|
||||
@@ -67,7 +67,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
|
||||
}
|
||||
|
||||
boolean timerAlreadyInitialized = getFromTime() != null && getToTime() != null && timeSlider.getCurrentValue() != null;
|
||||
if (!isTimeZoomed) {
|
||||
if (!timeRangeProvider.isZoomed()) {
|
||||
updateMinMax(raceTimesInfo);
|
||||
if (!timerAlreadyInitialized) {
|
||||
initTimerPosition(raceTimesInfo);
|
||||
@@ -85,9 +85,7 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
|
||||
@Override
|
||||
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
|
||||
super.onTimeZoomChanged(zoomStartTimepoint, zoomEndTimepoint);
|
||||
isTimeZoomed = true;
|
||||
timeSlider.setZoomed(true);
|
||||
// timer.setAutoAdvance(false);
|
||||
setMinMax(zoomStartTimepoint, zoomEndTimepoint, false);
|
||||
timeSlider.clearMarkersAndLabelsAndTicks();
|
||||
redrawAllMarkers(lastRaceTimesInfo);
|
||||
@@ -96,10 +94,13 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
|
||||
@Override
|
||||
public void onTimeZoomReset() {
|
||||
super.onTimeZoomReset();
|
||||
isTimeZoomed = false;
|
||||
timeSlider.setZoomed(false);
|
||||
// timer.setAutoAdvance(true);
|
||||
updateMinMax(this.lastRaceTimesInfo);
|
||||
|
||||
timeSlider.setMinValue(new Double(timeRangeProvider.getFromTime().getTime()), false);
|
||||
timeSlider.setMaxValue(new Double(timeRangeProvider.getToTime().getTime()), false);
|
||||
timeSlider.setCurrentValue(new Double(timer.getTime().getTime()), true);
|
||||
|
||||
// updateMinMax(this.lastRaceTimesInfo);
|
||||
timeSlider.clearMarkersAndLabelsAndTicks();
|
||||
redrawAllMarkers(lastRaceTimesInfo);
|
||||
}
|
||||
|
||||
+4
-2
@@ -269,6 +269,8 @@ public interface SailingService extends RemoteService {
|
||||
RaceEventLogDTO getRaceEventLog();
|
||||
|
||||
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor);
|
||||
|
||||
List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException;
|
||||
|
||||
List<Pair<String, List<CompetitorDTO>>> getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date) throws NoWindException;
|
||||
|
||||
List<String> getOverallLeaderboardNamesContaining(String leaderboardName);
|
||||
}
|
||||
+10
-2
@@ -421,6 +421,14 @@ public interface SailingServiceAsync {
|
||||
void getRankedCompetitorsFromBestToWorstAfterEachRaceColumn(String leaderboardName, Date date,
|
||||
AsyncCallback<List<Pair<String, List<CompetitorDTO>>>> callback);
|
||||
|
||||
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
|
||||
long stepSize, DetailType detailType, AsyncCallback<CompetitorsRaceDataDTO> callback);
|
||||
void getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
|
||||
long stepSize, DetailType detailType, AsyncCallback<CompetitorsRaceDataDTO> callback);
|
||||
|
||||
/**
|
||||
* Finds out the names of all {@link MetaLeaderboard}s managed by this server that
|
||||
* {@link MetaLeaderboard#getLeaderboards() contain} the leaderboard identified by <code>leaderboardName</code>. The
|
||||
* names of those meta-leaderboards are returned. The list returned is never <code>null</code> but may be empty if no such
|
||||
* leaderboard is found.
|
||||
*/
|
||||
void getOverallLeaderboardNamesContaining(String leaderboardName, AsyncCallback<List<String>> asyncCallback);
|
||||
}
|
||||
+9
@@ -439,4 +439,13 @@ public interface StringMessages extends Messages {
|
||||
String rankNAfterRace(long l, String string);
|
||||
String afterRace();
|
||||
String showRankChart();
|
||||
String overallLeaderboardSelection();
|
||||
String showOverallLeaderboardOnSamePage();
|
||||
String errorTryingToObtainOverallLeaderboards(String message);
|
||||
String finished();
|
||||
String passedTo(String name);
|
||||
String gapChangeSinceLegStartInSeconds();
|
||||
String sideToWhichMarkAtLegStartWasRounded();
|
||||
String raceIsLive(String raceName);
|
||||
String racesAreLive(String raceNames);
|
||||
}
|
||||
+10
-1
@@ -439,4 +439,13 @@ errorFetchingRankingChartData=Error fetching ranking chart data: {0}
|
||||
loadingCompetitorData=Loading competitor data...
|
||||
rankNAfterRace=Rank {0} after race {1}
|
||||
afterRace=after race
|
||||
showRankChart=Show rank chart
|
||||
showRankChart=Show rank chart
|
||||
overallLeaderboardSelection=Overall Leaderboard
|
||||
showOverallLeaderboardOnSamePage=Show overall leaderboard on same page
|
||||
errorTryingToObtainOverallLeaderboards=Error trying to obtain overall leaderboards: {0}
|
||||
finished=finished
|
||||
passedTo=passed to {0}
|
||||
gapChangeSinceLegStartInSeconds=Gap Change
|
||||
sideToWhichMarkAtLegStartWasRounded=Mark rounded to
|
||||
raceIsLive=Race {0} is live
|
||||
racesAreLive=Races {0} are live
|
||||
+10
-1
@@ -440,4 +440,13 @@ errorFetchingRankingChartData=Fehler beim Laden der Daten für das Ranglisten-Ch
|
||||
loadingCompetitorData=Lade Teilnehmer-Daten
|
||||
rankNAfterRace=Rang {0} nach Rennen {1}
|
||||
afterRace=nach Rennen
|
||||
showRankChart=Rang-Diagramm anzeigen
|
||||
showRankChart=Rang-Diagramm anzeigen
|
||||
overallLeaderboardSelection=Gesamtergebnisse
|
||||
showOverallLeaderboardOnSamePage=Gesamtergebnisse auf derselben Seite anzeigen
|
||||
errorTryingToObtainOverallLeaderboards=Fehler beim Laden der Gesamtergebnisse: {0}
|
||||
finished=beendet
|
||||
passedTo=an {0} gerundet
|
||||
gapChangeSinceLegStartInSeconds=Gap Änderung
|
||||
sideToWhichMarkAtLegStartWasRounded=Tonne gerundet an
|
||||
raceIsLive=Rennen {0} ist live
|
||||
racesAreLive=Rennen {0} sind live
|
||||
+28
-26
@@ -33,8 +33,7 @@ import com.sap.sailing.gwt.ui.shared.controls.slider.TimeSlider;
|
||||
public class TimePanel<T extends TimePanelSettings> extends FormPanel implements Component<T>, TimeListener, TimeZoomChangeListener,
|
||||
TimeRangeChangeListener, PlayStateListener, RequiresResize {
|
||||
protected final Timer timer;
|
||||
protected boolean isTimeZoomed;
|
||||
protected final TimeRangeProvider timeRangeProvider;
|
||||
protected final TimeRangeWithZoomProvider timeRangeProvider;
|
||||
|
||||
private final IntegerBox playSpeedBox;
|
||||
private final Label timeDelayLabel;
|
||||
@@ -83,11 +82,10 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
||||
}
|
||||
}
|
||||
|
||||
public TimePanel(Timer timer, TimeRangeProvider timeRangeProvider, StringMessages stringMessages) {
|
||||
public TimePanel(Timer timer, TimeRangeWithZoomProvider timeRangeProvider, StringMessages stringMessages) {
|
||||
this.timer = timer;
|
||||
this.timeRangeProvider = timeRangeProvider;
|
||||
this.stringMessages = stringMessages;
|
||||
isTimeZoomed = false;
|
||||
timer.addTimeListener(this);
|
||||
timer.addPlayStateListener(this);
|
||||
timeRangeProvider.addTimeRangeChangeListener(this);
|
||||
@@ -285,30 +283,32 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
||||
|
||||
@Override
|
||||
public void timeChanged(Date time) {
|
||||
if (getFromTime() != null && getToTime() != null) {
|
||||
// handle the case where time advances beyond slider's end.
|
||||
if (time.after(getToTime())) {
|
||||
switch (timer.getPlayMode()) {
|
||||
case Live:
|
||||
Date newMaxTime = new Date(time.getTime());
|
||||
if (newMaxTime.getTime() - getToTime().getTime() < MINIMUM_AUTO_ADVANCE_TIME_IN_MS) {
|
||||
newMaxTime.setTime(getToTime().getTime() + MINIMUM_AUTO_ADVANCE_TIME_IN_MS);
|
||||
if(timeRangeProvider.isZoomed()) {
|
||||
} else {
|
||||
if (getFromTime() != null && getToTime() != null) {
|
||||
// handle the case where time advances beyond slider's end.
|
||||
if (time.after(getToTime())) {
|
||||
switch (timer.getPlayMode()) {
|
||||
case Live:
|
||||
Date newMaxTime = new Date(time.getTime());
|
||||
if (newMaxTime.getTime() - getToTime().getTime() < MINIMUM_AUTO_ADVANCE_TIME_IN_MS) {
|
||||
newMaxTime.setTime(getToTime().getTime() + MINIMUM_AUTO_ADVANCE_TIME_IN_MS);
|
||||
}
|
||||
setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
|
||||
break;
|
||||
case Replay:
|
||||
timer.stop();
|
||||
break;
|
||||
}
|
||||
setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max
|
||||
break;
|
||||
case Replay:
|
||||
timer.stop();
|
||||
break;
|
||||
}
|
||||
timeSlider.setCurrentValue(new Double(time.getTime()), false);
|
||||
}
|
||||
long t = time.getTime();
|
||||
timeSlider.setCurrentValue(new Double(t), false);
|
||||
dateLabel.setText(dateFormatter.format(time));
|
||||
if (lastReceivedDataTimepoint == null) {
|
||||
timeLabel.setText(timeFormatter.format(time));
|
||||
} else {
|
||||
timeLabel.setText(timeFormatter.format(time) + " (" + timeFormatter.format(lastReceivedDataTimepoint) + ")");
|
||||
}
|
||||
}
|
||||
dateLabel.setText(dateFormatter.format(time));
|
||||
if (lastReceivedDataTimepoint == null) {
|
||||
timeLabel.setText(timeFormatter.format(time));
|
||||
} else {
|
||||
timeLabel.setText(timeFormatter.format(time) + " (" + timeFormatter.format(lastReceivedDataTimepoint) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +340,9 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
timeRangeProvider.setTimeRange(min, max, this);
|
||||
if(!timeRangeProvider.isZoomed()) {
|
||||
timeRangeProvider.setTimeRange(min, max, this);
|
||||
}
|
||||
|
||||
int numSteps = timeSlider.getElement().getClientWidth();
|
||||
if (numSteps > 0) {
|
||||
|
||||
+5
@@ -57,4 +57,9 @@ public class TimeRangeModel implements TimeRangeProvider {
|
||||
public Date getToTime() {
|
||||
return toTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeRangeModel [fromTime=" + fromTime + ", toTime=" + toTime + "]";
|
||||
}
|
||||
}
|
||||
+8
@@ -42,6 +42,8 @@ public class TimeRangeWithZoomModel extends TimeRangeModel implements TimeRangeW
|
||||
|
||||
public void resetTimeZoom(TimeZoomChangeListener... listenersNotToNotify) {
|
||||
isTimeZoomed = false;
|
||||
this.zoomStartTimepoint = null;
|
||||
this.zoomEndTimepoint = null;
|
||||
for (TimeZoomChangeListener listener : listeners) {
|
||||
if (listenersNotToNotify == null || !Arrays.asList(listenersNotToNotify).contains(listener)) {
|
||||
listener.onTimeZoomReset();
|
||||
@@ -56,4 +58,10 @@ public class TimeRangeWithZoomModel extends TimeRangeModel implements TimeRangeW
|
||||
public boolean isZoomed() {
|
||||
return isTimeZoomed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TimeRangeWithZoomModel [fromTime=" + fromTime + ", toTime=" + toTime + ", zoomStartTimepoint=" + zoomStartTimepoint + ", zoomEndTimepoint="
|
||||
+ zoomEndTimepoint + ", isTimeZoomed=" + isTimeZoomed + "]";
|
||||
}
|
||||
}
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
package com.sap.sailing.gwt.ui.client;
|
||||
|
||||
public interface WindowSizeDetector {
|
||||
|
||||
boolean isSmallWidth();
|
||||
|
||||
}
|
||||
+6
-10
@@ -16,10 +16,10 @@ import com.google.gwt.user.client.ui.Widget;
|
||||
*/
|
||||
public class DialogBoxExt extends DialogBox {
|
||||
|
||||
private HorizontalPanel captionPanel = new HorizontalPanel();
|
||||
private final HorizontalPanel captionPanel;
|
||||
|
||||
/** widget which will be use to close the dialog box */
|
||||
private Widget closeWidget = null;
|
||||
private final Widget closeWidget;
|
||||
|
||||
/**
|
||||
* You have to provide a widget here. If click on the widget the dialog box will be closed.
|
||||
@@ -27,21 +27,17 @@ public class DialogBoxExt extends DialogBox {
|
||||
*/
|
||||
public DialogBoxExt(Widget closeDialogBox) {
|
||||
super();
|
||||
|
||||
assert closeDialogBox != null;
|
||||
captionPanel = new HorizontalPanel();
|
||||
closeWidget = closeDialogBox;
|
||||
closeWidget.getElement().addClassName("chartCloseButton");
|
||||
|
||||
// an empty header could case a problem!
|
||||
// an empty header could cause a problem!
|
||||
setHTML(" ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHTML(String html) {
|
||||
if (closeWidget != null) {
|
||||
setCaption(html, closeWidget);
|
||||
} else {
|
||||
super.setHTML(html);
|
||||
}
|
||||
setCaption(html, closeWidget);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+32
-15
@@ -10,7 +10,6 @@ import com.google.gwt.dom.client.Style.Unit;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel;
|
||||
import com.google.gwt.user.client.ui.FlowPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.RootLayoutPanel;
|
||||
import com.google.gwt.user.client.ui.RootPanel;
|
||||
@@ -46,6 +45,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
private static final String PARAM_AUTO_EXPAND_LAST_RACE_COLUMN = "autoExpandLastRaceColumn";
|
||||
private static final String PARAM_REGATTA_NAME = "regattaName";
|
||||
private static final String PARAM_REFRESH_INTERVAL_MILLIS = "refreshIntervalMillis";
|
||||
private static final String PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE = "showOverallLeaderboardsOnSamePage";
|
||||
private static final String PARAM_DELAY_TO_LIVE_MILLIS = "delayToLiveMillis";
|
||||
|
||||
/**
|
||||
@@ -55,6 +55,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
private static final String PARAM_NAME_LAST_N = "lastN";
|
||||
private String leaderboardName;
|
||||
private String leaderboardGroupName;
|
||||
private GlobalNavigationPanel globalNavigationPanel;
|
||||
|
||||
@Override
|
||||
public void onModuleLoad() {
|
||||
@@ -91,15 +92,25 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
if (!embedded) {
|
||||
// Hack to shorten the leaderboardName in case of overall leaderboards
|
||||
String leaderboardDisplayName = Window.Location.getParameter("displayName");
|
||||
if(leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
|
||||
if (leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
|
||||
leaderboardDisplayName = leaderboardName;
|
||||
}
|
||||
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages);
|
||||
globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, null, leaderboardGroupName);
|
||||
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages, this) {
|
||||
@Override
|
||||
public void onResize() {
|
||||
super.onResize();
|
||||
if (isSmallWidth()) {
|
||||
remove(globalNavigationPanel);
|
||||
} else {
|
||||
add(globalNavigationPanel);
|
||||
}
|
||||
}
|
||||
};
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
|
||||
FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, null, leaderboardGroupName);
|
||||
logoAndTitlePanel.add(globalNavigationPanel);
|
||||
|
||||
if (!isSmallWidth()) {
|
||||
logoAndTitlePanel.add(globalNavigationPanel);
|
||||
}
|
||||
mainPanel.addNorth(logoAndTitlePanel, 68);
|
||||
}
|
||||
ScrollPanel contentScrollPanel = new ScrollPanel();
|
||||
@@ -114,10 +125,10 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(),
|
||||
leaderboardSettings,
|
||||
preselectedRace, new CompetitorSelectionModel(
|
||||
/* hasMultiSelection */true), timer, leaderboardName, leaderboardGroupName,
|
||||
LeaderboardEntryPoint.this, stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */ null,
|
||||
Window.Location.getParameterMap().containsKey(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN) ?
|
||||
/* hasMultiSelection */true), timer, leaderboardName, LeaderboardEntryPoint.this,
|
||||
stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, Window.Location.getParameterMap().containsKey(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN) ?
|
||||
Boolean.valueOf(Window.Location.getParameterMap().get(PARAM_AUTO_EXPAND_LAST_RACE_COLUMN).get(0)) : false);
|
||||
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
|
||||
contentScrollPanel.setWidget(leaderboardPanel);
|
||||
|
||||
mainPanel.add(contentScrollPanel);
|
||||
@@ -153,6 +164,9 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
} else {
|
||||
numberOfLastRacesToShow = null;
|
||||
}
|
||||
boolean showOverallLeaderboardsOnSamePage = parameterMap.containsKey(PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE) ?
|
||||
Boolean.valueOf(parameterMap.get(PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE).get(0)) :
|
||||
false;
|
||||
if (parameterMap.containsKey(PARAM_RACE_NAME) || parameterMap.containsKey(PARAM_RACE_DETAIL) ||
|
||||
parameterMap.containsKey(PARAM_LEG_DETAIL) || parameterMap.containsKey(PARAM_MANEUVER_DETAIL) ||
|
||||
parameterMap.containsKey(PARAM_OVERALL_DETAIL)) {
|
||||
@@ -171,12 +185,13 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
/* sort by column */ (namesOfRacesToShow != null && !namesOfRacesToShow.isEmpty()) ?
|
||||
namesOfRacesToShow.get(0) : null, /* ascending */ true,
|
||||
/* updateUponPlayStateChange */ raceDetails.isEmpty() && legDetails.isEmpty(),
|
||||
raceColumnSelectionStrategy);
|
||||
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
|
||||
} else {
|
||||
final List<DetailType> overallDetails = Collections.emptyList();
|
||||
result = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, /* overallDetails */
|
||||
overallDetails, null,
|
||||
/* autoExpandFirstRace */ false, refreshIntervalMillis, numberOfLastRacesToShow, raceColumnSelectionStrategy);
|
||||
result = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null,
|
||||
/* overallDetails */ overallDetails, null,
|
||||
/* autoExpandFirstRace */false, refreshIntervalMillis, numberOfLastRacesToShow,
|
||||
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -285,7 +300,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
StringBuilder maneuverDetails = new StringBuilder();
|
||||
for (DetailType maneuverDetail : settings.getLeaderboardSettings().getManeuverDetailsToShow()) {
|
||||
maneuverDetails.append('&');
|
||||
maneuverDetails.append(PARAM_RACE_DETAIL);
|
||||
maneuverDetails.append(PARAM_MANEUVER_DETAIL);
|
||||
maneuverDetails.append('=');
|
||||
maneuverDetails.append(maneuverDetail.name());
|
||||
}
|
||||
@@ -293,6 +308,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
String link = URLFactory.INSTANCE.encode("/gwt/Leaderboard.html?name=" + leaderboardName
|
||||
+ (settings.isShowRaceDetails() ? "&"+PARAM_SHOW_RACE_DETAILS+"=true" : "")
|
||||
+ (settings.isEmbedded() ? "&"+PARAM_EMBEDDED+"=true" : "")
|
||||
+ (settings.getLeaderboardSettings().isShowOverallLeaderboardsOnSamePage() ? "&"+PARAM_SHOW_OVERALL_LEADERBOARDS_ON_SAME_PAGE+"=true" : "")
|
||||
+ (settings.getLeaderboardSettings().getDelayInMilliseconds() == null &&
|
||||
settings.getLeaderboardSettings().getDelayInMilliseconds() != 0 ? "" :
|
||||
"&"+PARAM_DELAY_TO_LIVE_MILLIS+"="+settings.getLeaderboardSettings().getDelayInMilliseconds())
|
||||
@@ -302,6 +318,7 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint {
|
||||
+ legDetails.toString()
|
||||
+ raceDetails.toString()
|
||||
+ overallDetails.toString()
|
||||
+ maneuverDetails.toString()
|
||||
+ (settings.isAutoExpandLastRaceColumn() ? "&"+PARAM_AUTO_EXPAND_LAST_RACE_COLUMN+"=true" : "")
|
||||
+ (settings.getLeaderboardSettings().getNumberOfLastRacesToShow() == null ? "" :
|
||||
"&"+PARAM_NAME_LAST_N+"="+settings.getLeaderboardSettings().getNumberOfLastRacesToShow())
|
||||
|
||||
+199
-27
@@ -16,11 +16,13 @@ import com.google.gwt.cell.client.CompositeCell;
|
||||
import com.google.gwt.cell.client.EditTextCell;
|
||||
import com.google.gwt.cell.client.TextCell;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.dom.client.Style.FontWeight;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.NumberFormat;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
|
||||
import com.google.gwt.safehtml.shared.SafeHtml;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
import com.google.gwt.user.cellview.client.Column;
|
||||
@@ -53,10 +55,12 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.SortingOrder;
|
||||
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
|
||||
import com.sap.sailing.domain.common.impl.Util;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
|
||||
import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
|
||||
import com.sap.sailing.gwt.ui.client.Collator;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
||||
import com.sap.sailing.gwt.ui.client.FlagImageResolver;
|
||||
@@ -105,6 +109,24 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
|
||||
private final SailingServiceAsync sailingService;
|
||||
|
||||
private static String IS_LIVE_TEXT_COLOR = "#1876B3";
|
||||
private static String DEFAULT_TEXT_COLOR = "#000000";
|
||||
|
||||
private static final String STYLE_LEADERBOARD_CONTENT = "leaderboardContent";
|
||||
private static final String STYLE_LEADERBOARD_INFO = "leaderboardInfo";
|
||||
private static final String STYLE_LEADERBOARD_TOOLBAR = "leaderboardContent-toolbar";
|
||||
private static final String STYLE_LEADERBOARD_LIVE_RACE = "leaderboardContent-liverace";
|
||||
|
||||
interface RaceColumnTemplates extends SafeHtmlTemplates {
|
||||
@SafeHtmlTemplates.Template("<div style=\"color:{0}; border-bottom: 3px solid {1}\">")
|
||||
SafeHtml cellFrameWithTextColorAndFleetBorder(String textColor, String borderStyle);
|
||||
|
||||
@SafeHtmlTemplates.Template("<div style=\"color:{0};\">")
|
||||
SafeHtml cellFrameWithTextColor(String textColor);
|
||||
}
|
||||
|
||||
private static RaceColumnTemplates raceColumnTemplate = GWT.create(RaceColumnTemplates.class);
|
||||
|
||||
/**
|
||||
* The leaderboard name is used to
|
||||
* {@link SailingServiceAsync#getLeaderboardByName(String, java.util.Date, String[], com.google.gwt.user.client.rpc.AsyncCallback)
|
||||
@@ -162,11 +184,15 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
|
||||
protected final String TOTAL_COLUMN_STYLE;
|
||||
|
||||
public static final String LEADERBOARD_MARGIN_STYLE = "leaderboardMargin";
|
||||
|
||||
private final Timer timer;
|
||||
|
||||
private boolean autoExpandPreSelectedRace;
|
||||
|
||||
private boolean autoExpandLastRaceColumn;
|
||||
|
||||
private boolean showOverallLeaderboardsOnSamePage;
|
||||
|
||||
/**
|
||||
* Remembers whether the auto-expand of the pre-selected race (see {@link #autoExpandPreSelectedRace}) or last
|
||||
@@ -191,12 +217,26 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
private final RaceIdentifier preSelectedRace;
|
||||
|
||||
private final VerticalPanel contentPanel;
|
||||
|
||||
/**
|
||||
* Used to display one or more overall leaderboards if selected. See also
|
||||
* {@link LeaderboardSettings#isShowOverallLeaderboardsOnSamePage()}. Always contained by the {@link #contentPanel} at the
|
||||
* right position, but may be empty.
|
||||
*/
|
||||
private final VerticalPanel overallLeaderboardsPanel;
|
||||
|
||||
/**
|
||||
* The leaderboard panels displayed in {@link #overallLeaderboardsPanel}.
|
||||
*/
|
||||
private final List<LeaderboardPanel> overallLeaderboardPanels;
|
||||
|
||||
private final HorizontalPanel refreshAndSettingsPanel;
|
||||
|
||||
private final FlowPanel informationPanel;
|
||||
private final Label scoreCorrectionLastUpdateTimeLabel;
|
||||
private final Label scoreCorrectionCommentLabel;
|
||||
|
||||
private final Label liveRaceLabel;
|
||||
|
||||
private final DateTimeFormat dateFormatter = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_LONG);
|
||||
private final DateTimeFormat timeFormatter = DateTimeFormat.getFormat("HH:mm:ss");
|
||||
|
||||
@@ -247,6 +287,8 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
*/
|
||||
private RaceTimesInfoProvider raceTimesInfoProvider;
|
||||
|
||||
private final UserAgentDetails userAgent;
|
||||
|
||||
private class SettingsClickHandler implements ClickHandler {
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
@@ -284,6 +326,11 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getLegendWidget() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmbedded() {
|
||||
return isEmbedded;
|
||||
@@ -390,6 +437,67 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true);
|
||||
}
|
||||
}
|
||||
if (newSettings.isShowOverallLeaderboardsOnSamePage() != showOverallLeaderboardsOnSamePage) {
|
||||
showOverallLeaderboardsOnSamePage = newSettings.isShowOverallLeaderboardsOnSamePage();
|
||||
if (showOverallLeaderboardsOnSamePage) {
|
||||
showOverallLeaderboards();
|
||||
} else {
|
||||
hideOverallLeaderboards();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showOverallLeaderboards() {
|
||||
overallLeaderboardsPanel.clear();
|
||||
final Label overallStandingsLabel = new Label(stringMessages.overallStandings());
|
||||
overallStandingsLabel.setStyleName("leaderboardHeading");
|
||||
overallLeaderboardsPanel.add(overallStandingsLabel);
|
||||
updateOverallLeaderboardsAndAddToPanel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches from the server the list of overall ("meta") leaderboard descriptions in which this main leaderboard occurs and
|
||||
* creates one {@link LeaderboardPanel} for each such overall leaderboard. Those panels are then added to
|
||||
* {@link #overallLeaderboardPanels}, registered with the {@link #timer} for common timing with the main leaderboard panel,
|
||||
* and the panels are made visible by adding them to the {@link #overallLeaderboardsPanel} panel.
|
||||
*/
|
||||
private void updateOverallLeaderboardsAndAddToPanel() {
|
||||
getSailingService().getOverallLeaderboardNamesContaining(leaderboardName, new AsyncCallback<List<String>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
getErrorReporter().reportError(
|
||||
stringMessages.errorTryingToObtainOverallLeaderboards(caught.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<String> overallLeaderboardNames) {
|
||||
for (String overallLeaderboardName : overallLeaderboardNames) {
|
||||
LeaderboardPanel overallLeaderboardPanel = new LeaderboardPanel(
|
||||
getSailingService(), asyncActionsExecutor, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
/* namesOfRaceColumnsToShow */ null, /* namesOfRacesToShow */ null, /* nameOfRaceToSort */ null,
|
||||
/* autoExpandPreSelectedRace */ false, /* showOverallLeaderboardsOnSamePage */ false),
|
||||
/* preSelectedRace */ null, new CompetitorSelectionModel(/* hasMultiSelection */ true),
|
||||
timer, overallLeaderboardName, errorReporter, stringMessages, userAgent, showRaceDetails,
|
||||
/* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false);
|
||||
overallLeaderboardPanels.add(overallLeaderboardPanel);
|
||||
overallLeaderboardsPanel.add(overallLeaderboardPanel);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the panel used to display the overall leaderboards, removes all overall leaderboard panels from their listener
|
||||
* relationship with the {@link #timer} and then clears the {@link #overallLeaderboardPanels} list.
|
||||
*/
|
||||
private void hideOverallLeaderboards() {
|
||||
overallLeaderboardsPanel.clear();
|
||||
for (LeaderboardPanel overallLeaderboardPanel : overallLeaderboardPanels) {
|
||||
timer.removeTimeListener(overallLeaderboardPanel);
|
||||
timer.removePlayStateListener(overallLeaderboardPanel);
|
||||
}
|
||||
overallLeaderboardPanels.clear();
|
||||
}
|
||||
|
||||
private void setRaceColumnSelectionToLastNStrategy(final Integer numberOfLastRacesToShow) {
|
||||
@@ -554,6 +662,10 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
public RaceColumnDTO getRace() {
|
||||
return race;
|
||||
}
|
||||
|
||||
public void setRace(RaceColumnDTO race) {
|
||||
this.race = race;
|
||||
}
|
||||
|
||||
public String getRaceColumnName() {
|
||||
return race.getRaceColumnName();
|
||||
@@ -563,6 +675,10 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
return race.isMedalRace();
|
||||
}
|
||||
|
||||
public boolean isLive(FleetDTO fleetDTO) {
|
||||
return race.isLive(fleetDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnStyle() {
|
||||
return columnStyle;
|
||||
@@ -585,15 +701,18 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder html) {
|
||||
LeaderboardEntryDTO entry = object.fieldsByRaceColumnName.get(getRaceColumnName());
|
||||
if (entry != null) {
|
||||
String fleetColorBarStyle = "";
|
||||
boolean isLive = isLive(entry.fleet);
|
||||
|
||||
String textColor = isLive ? IS_LIVE_TEXT_COLOR : DEFAULT_TEXT_COLOR;
|
||||
String totalPointsAsText = entry.totalPoints == null ? "" : scoreFormat.format(entry.totalPoints);
|
||||
String netPointsAsText = entry.netPoints == null ? "" : scoreFormat.format(entry.netPoints);
|
||||
|
||||
if (entry.fleet != null && entry.fleet.getColor() != null) {
|
||||
fleetColorBarStyle = " style=\"border-bottom: 3px solid " + entry.fleet.getColor().getAsHtml()
|
||||
+ ";\"";
|
||||
html.append(raceColumnTemplate.cellFrameWithTextColorAndFleetBorder(textColor, entry.fleet.getColor().getAsHtml()));
|
||||
} else {
|
||||
html.append(raceColumnTemplate.cellFrameWithTextColor(textColor));
|
||||
}
|
||||
html.appendHtmlConstant("<div" + fleetColorBarStyle + ">");
|
||||
|
||||
// don't show points if max points / penalty
|
||||
if (entry.reasonForMaxPoints == null || entry.reasonForMaxPoints == MaxPointsReason.NONE) {
|
||||
if (!entry.discarded) {
|
||||
@@ -611,8 +730,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
if (entry.discarded) {
|
||||
html.appendHtmlConstant("<del>");
|
||||
}
|
||||
html.appendEscaped(entry.reasonForMaxPoints == MaxPointsReason.NONE ? "" : entry.reasonForMaxPoints
|
||||
.name());
|
||||
html.appendEscaped(entry.reasonForMaxPoints == MaxPointsReason.NONE ? "" : entry.reasonForMaxPoints.name());
|
||||
if (entry.discarded) {
|
||||
html.appendHtmlConstant("</del>");
|
||||
}
|
||||
@@ -653,8 +771,9 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
}
|
||||
|
||||
public static DetailType[] getAvailableRaceDetailColumnTypes() {
|
||||
return new DetailType[] { DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
DetailType.RACE_DISTANCE_TRAVELED, DetailType.RACE_GAP_TO_LEADER_IN_SECONDS,
|
||||
return new DetailType[] { DetailType.RACE_GAP_TO_LEADER_IN_SECONDS,
|
||||
DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
DetailType.RACE_DISTANCE_TRAVELED,
|
||||
DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS,
|
||||
DetailType.NUMBER_OF_MANEUVERS, DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG,
|
||||
DetailType.RACE_AVERAGE_CROSS_TRACK_ERROR_IN_METERS };
|
||||
@@ -996,7 +1115,9 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
|
||||
@Override
|
||||
public void render(Context context, LeaderboardRowDTO object, SafeHtmlBuilder sb) {
|
||||
sb.appendHtmlConstant("<span style=\"font-weight: bold;\">");
|
||||
String textColor = getLeaderboard().hasLiveRace() ? IS_LIVE_TEXT_COLOR : DEFAULT_TEXT_COLOR;
|
||||
|
||||
sb.appendHtmlConstant("<span style=\"font-weight: bold; color:" + textColor + "\">");
|
||||
sb.appendEscaped(getValue(object));
|
||||
sb.appendHtmlConstant("</span>");
|
||||
}
|
||||
@@ -1096,29 +1217,28 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
|
||||
public LeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
LeaderboardSettings settings, CompetitorSelectionProvider competitorSelectionProvider,
|
||||
String leaderboardName, String leaderboardGroupName, ErrorReporter errorReporter,
|
||||
final StringMessages stringMessages, final UserAgentDetails userAgent, boolean showRaceDetails) {
|
||||
String leaderboardName, ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
final UserAgentDetails userAgent, boolean showRaceDetails) {
|
||||
this(sailingService, asyncActionsExecutor, settings, /* preSelectedRace */null, competitorSelectionProvider,
|
||||
leaderboardName, leaderboardGroupName, errorReporter, stringMessages, userAgent, showRaceDetails);
|
||||
leaderboardName, errorReporter, stringMessages, userAgent, showRaceDetails);
|
||||
}
|
||||
|
||||
public LeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
LeaderboardSettings settings, RaceIdentifier preSelectedRace,
|
||||
CompetitorSelectionProvider competitorSelectionProvider, String leaderboardName,
|
||||
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
final UserAgentDetails userAgent, boolean showRaceDetails) {
|
||||
ErrorReporter errorReporter, final StringMessages stringMessages, final UserAgentDetails userAgent,
|
||||
boolean showRaceDetails) {
|
||||
this(sailingService, asyncActionsExecutor, settings, preSelectedRace, competitorSelectionProvider, new Timer(
|
||||
PlayModes.Replay, /* delayBetweenAutoAdvancesInMilliseconds */3000l), leaderboardName,
|
||||
leaderboardGroupName, errorReporter, stringMessages, userAgent, showRaceDetails,
|
||||
/* optionalRaceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false);
|
||||
errorReporter, stringMessages, userAgent, showRaceDetails, /* optionalRaceTimesInfoProvider */ null,
|
||||
/* autoExpandLastRaceColumn */ false);
|
||||
}
|
||||
|
||||
public LeaderboardPanel(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
|
||||
LeaderboardSettings settings, RaceIdentifier preSelectedRace,
|
||||
CompetitorSelectionProvider competitorSelectionProvider, Timer timer, String leaderboardName,
|
||||
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
final UserAgentDetails userAgent, boolean showRaceDetails, RaceTimesInfoProvider optionalRaceTimesInfoProvider,
|
||||
boolean autoExpandLastRaceColumn) {
|
||||
ErrorReporter errorReporter, final StringMessages stringMessages, final UserAgentDetails userAgent,
|
||||
boolean showRaceDetails, RaceTimesInfoProvider optionalRaceTimesInfoProvider, boolean autoExpandLastRaceColumn) {
|
||||
this.showRaceDetails = showRaceDetails;
|
||||
this.sailingService = sailingService;
|
||||
this.asyncActionsExecutor = asyncActionsExecutor;
|
||||
@@ -1133,6 +1253,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
this.selectedOverallDetailColumns = new ArrayList<DetailType>();
|
||||
this.raceTimesInfoProvider = optionalRaceTimesInfoProvider;
|
||||
this.selectedManeuverDetails = new ArrayList<DetailType>();
|
||||
this.showOverallLeaderboardsOnSamePage = settings.isShowOverallLeaderboardsOnSamePage();
|
||||
overallDetailColumnMap = createOverallDetailColumnMap();
|
||||
settingsUpdatedExplicitly = !settings.updateUponPlayStateChange();
|
||||
if (settings.getLegDetailsToShow() != null) {
|
||||
@@ -1178,6 +1299,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
leaderboardTable = new SortedCellTableWithStylableHeaders<LeaderboardRowDTO>(
|
||||
/* pageSize */10000, tableResources);
|
||||
getLeaderboardTable().setWidth("100%");
|
||||
this.userAgent = userAgent;
|
||||
if (userAgent.isMobile() == UserAgentDetails.PlatformTypes.MOBILE) {
|
||||
leaderboardSelectionModel = new ToggleSelectionModel<LeaderboardRowDTO>();
|
||||
} else {
|
||||
@@ -1203,16 +1325,28 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
isEmbedded = true;
|
||||
}
|
||||
contentPanel = new VerticalPanel();
|
||||
contentPanel.setStyleName("leaderboardContent");
|
||||
contentPanel.setStyleName(STYLE_LEADERBOARD_CONTENT);
|
||||
overallLeaderboardsPanel = new VerticalPanel();
|
||||
overallLeaderboardPanels = new ArrayList<LeaderboardPanel>();
|
||||
|
||||
// the information panel
|
||||
informationPanel = new FlowPanel();
|
||||
informationPanel.setStyleName("leaderboardInfo");
|
||||
informationPanel.setStyleName(STYLE_LEADERBOARD_INFO);
|
||||
scoreCorrectionLastUpdateTimeLabel = new Label("");
|
||||
scoreCorrectionCommentLabel = new Label("");
|
||||
informationPanel.add(scoreCorrectionCommentLabel);
|
||||
informationPanel.add(scoreCorrectionLastUpdateTimeLabel);
|
||||
|
||||
liveRaceLabel = new Label(stringMessages.live());
|
||||
liveRaceLabel.setStyleName(STYLE_LEADERBOARD_LIVE_RACE);
|
||||
liveRaceLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD);
|
||||
liveRaceLabel.getElement().getStyle().setColor(IS_LIVE_TEXT_COLOR);
|
||||
liveRaceLabel.setVisible(false);
|
||||
informationPanel.add(liveRaceLabel);
|
||||
|
||||
// the toolbar panel
|
||||
DockPanel toolbarPanel = new DockPanel();
|
||||
toolbarPanel.setStyleName("leaderboardContent-toolbar");
|
||||
toolbarPanel.setStyleName(STYLE_LEADERBOARD_TOOLBAR);
|
||||
busyIndicator = new SimpleBusyIndicator(false, 0.8f);
|
||||
if (!isEmbedded) {
|
||||
toolbarPanel.add(informationPanel, DockPanel.WEST);
|
||||
@@ -1276,6 +1410,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
contentPanel.add(toolbarPanel);
|
||||
}
|
||||
contentPanel.add(getLeaderboardTable());
|
||||
contentPanel.add(overallLeaderboardsPanel);
|
||||
setWidget(contentPanel);
|
||||
raceNameForDefaultSorting = settings.getNameOfRaceToSort();
|
||||
}
|
||||
@@ -1523,6 +1658,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
raceColumnSelection.autoUpdateRaceColumnSelectionForUpdatedLeaderboard(getLeaderboard(), leaderboard);
|
||||
setLeaderboard(leaderboard);
|
||||
adjustColumnLayout(leaderboard);
|
||||
updateRaceColumnDTOsToRaceColumns(leaderboard);
|
||||
for (RaceColumn<?> columnToCollapseAndExpandAgain : columnsToCollapseAndExpandAgain) {
|
||||
columnToCollapseAndExpandAgain.toggleExpansion();
|
||||
}
|
||||
@@ -1563,19 +1699,55 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
leaderboardSelectionModel.setSelected(row, true);
|
||||
}
|
||||
}
|
||||
|
||||
scoreCorrectionCommentLabel.setText(leaderboard.getComment() != null ? leaderboard.getComment() : "");
|
||||
if (leaderboard.getTimePointOfLastCorrectionsValidity() != null) {
|
||||
Date lastCorrectionDate = leaderboard.getTimePointOfLastCorrectionsValidity();
|
||||
String lastUpdate = dateFormatter.format(lastCorrectionDate) + " "
|
||||
String lastUpdate = dateFormatter.format(lastCorrectionDate) + ", "
|
||||
+ timeFormatter.format(lastCorrectionDate);
|
||||
scoreCorrectionLastUpdateTimeLabel.setText(stringMessages.lastScoreUpdate() + ": " + lastUpdate);
|
||||
} else {
|
||||
scoreCorrectionLastUpdateTimeLabel.setText("");
|
||||
}
|
||||
|
||||
List<Pair<RaceColumnDTO, FleetDTO>> liveRaces = leaderboard.getLiveRaces();
|
||||
boolean hasLiveRace = !liveRaces.isEmpty();
|
||||
if (hasLiveRace) {
|
||||
String liveRaceText = "";
|
||||
if(liveRaces.size() == 1) {
|
||||
Pair<RaceColumnDTO, FleetDTO> liveRace = liveRaces.get(0);
|
||||
liveRaceText = stringMessages.raceIsLive("'" + liveRace.getA().getRaceColumnName() + "'");
|
||||
} else {
|
||||
String raceNames = "";
|
||||
for(Pair<RaceColumnDTO, FleetDTO> liveRace: liveRaces) {
|
||||
raceNames += "'" + liveRace.getA().getRaceColumnName() + "', ";
|
||||
}
|
||||
// remove last ", "
|
||||
raceNames = raceNames.substring(0, raceNames.length()-2);
|
||||
liveRaceText = stringMessages.racesAreLive(raceNames);
|
||||
}
|
||||
liveRaceLabel.setText(liveRaceText);
|
||||
} else {
|
||||
liveRaceLabel.setText("");
|
||||
}
|
||||
scoreCorrectionLastUpdateTimeLabel.setVisible(!hasLiveRace);
|
||||
liveRaceLabel.setVisible(hasLiveRace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The race columns hold a now outdated copy of a {@link RaceColumnDTO} which needs to be updated from the {@link LeaderboardDTO} just received
|
||||
*/
|
||||
private void updateRaceColumnDTOsToRaceColumns(LeaderboardDTO leaderboard) {
|
||||
for (RaceColumnDTO newRace : leaderboard.getRaceList()) {
|
||||
RaceColumn<?> raceColumn = getRaceColumnByRaceColumnName(newRace.name);
|
||||
if (raceColumn != null) {
|
||||
raceColumn.setRace(newRace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Due to a course change, a race may change its number of legs. All expanded race columns that show leg columns and
|
||||
* whose leg count changed need to be collapsed before the leaderboard is replaced, and expanded afterwards again.
|
||||
* Race columns whose toggling is {@link ExpandableSortableColumn#isTogglingInProcess() currently in progress} are
|
||||
@@ -1755,7 +1927,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
}
|
||||
|
||||
/**
|
||||
* If header information doesn't match the race column's actual state (tracked races attached meaning expanable;
|
||||
* If header information doesn't match the race column's actual state (tracked races attached meaning expandable;
|
||||
* medal race), the column is removed and inserted again
|
||||
*
|
||||
* @param raceColumn
|
||||
@@ -2076,7 +2248,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
: preSelectedRace.getRaceName(),
|
||||
/* don't change nameOfRaceColumnToShow */null,
|
||||
/* set nameOfRaceToShow if race was pre-selected */preSelectedRace == null ? null : preSelectedRace
|
||||
.getRaceName(), getRaceColumnSelection()));
|
||||
.getRaceName(), getRaceColumnSelection(), showOverallLeaderboardsOnSamePage));
|
||||
}
|
||||
currentlyHandlingPlayStateChange = false;
|
||||
oldPlayMode = playMode;
|
||||
@@ -2129,7 +2301,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta
|
||||
Collections.unmodifiableList(selectedLegDetails), Collections.unmodifiableList(selectedRaceDetails),
|
||||
Collections.unmodifiableList(selectedOverallDetailColumns), /* All races to select */ leaderboard.getRaceList(),
|
||||
raceColumnSelection.getSelectedRaceColumnsOrderedAsInLeaderboard(leaderboard), raceColumnSelection, autoExpandPreSelectedRace,
|
||||
timer.getRefreshInterval(), timer.getLivePlayDelayInMillis(), stringMessages);
|
||||
showOverallLeaderboardsOnSamePage, timer.getRefreshInterval(), timer.getLivePlayDelayInMillis(), stringMessages);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-1
@@ -30,6 +30,7 @@ public class LeaderboardSettings {
|
||||
private final Long delayBetweenAutoAdvancesInMilliseconds;
|
||||
private final Long delayInMilliseconds;
|
||||
private final boolean updateUponPlayStateChange;
|
||||
private final boolean showOverallLeaderboardsOnSamePage;
|
||||
|
||||
/**
|
||||
* There are two ways to select race columns.
|
||||
@@ -52,7 +53,8 @@ public class LeaderboardSettings {
|
||||
List<DetailType> raceDetailsToShow, List<DetailType> overallDetailsToShow,
|
||||
List<String> namesOfRaceColumnsToShow, List<String> namesOfRacesToShow, Integer numberOfLastRacesToShow,
|
||||
boolean autoExpandPreSelectedRace, Long delayBetweenAutoAdvancesInMilliseconds, Long delayInMilliseconds,
|
||||
String nameOfRaceToSort, boolean sortAscending, boolean updateUponPlayStateChange, RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy) {
|
||||
String nameOfRaceToSort, boolean sortAscending, boolean updateUponPlayStateChange,
|
||||
RaceColumnSelectionStrategies activeRaceColumnSelectionStrategy, boolean showOverallLeaderboardsOnSamePage) {
|
||||
if (namesOfRacesToShow != null && namesOfRaceColumnsToShow != null) {
|
||||
throw new IllegalArgumentException("You can identify races either only by their race or by their column names, not both");
|
||||
}
|
||||
@@ -70,6 +72,7 @@ public class LeaderboardSettings {
|
||||
this.nameOfRaceToSort = nameOfRaceToSort;
|
||||
this.sortAscending = sortAscending;
|
||||
this.updateUponPlayStateChange = updateUponPlayStateChange;
|
||||
this.showOverallLeaderboardsOnSamePage = showOverallLeaderboardsOnSamePage;
|
||||
}
|
||||
|
||||
public List<DetailType> getManeuverDetailsToShow() {
|
||||
@@ -151,4 +154,7 @@ public class LeaderboardSettings {
|
||||
return activeRaceColumnSelectionStrategy;
|
||||
}
|
||||
|
||||
public boolean isShowOverallLeaderboardsOnSamePage() {
|
||||
return showOverallLeaderboardsOnSamePage;
|
||||
}
|
||||
}
|
||||
+25
-5
@@ -46,6 +46,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
private LongBox refreshIntervalInSecondsBox;
|
||||
private LongBox delayInSecondsBox;
|
||||
private final boolean autoExpandPreSelectedRace;
|
||||
private final boolean showOverallLeaderboardOnSamePage;
|
||||
private final long delayBetweenAutoAdvancesInMilliseconds;
|
||||
private final long delayInMilliseconds;
|
||||
private final Integer numberOfLastRacesToShow;
|
||||
@@ -53,11 +54,14 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
private RadioButton explicitRaceColumnSelectionRadioBtn;
|
||||
private RadioButton lastNRacesColumnSelectionRadioBtn;
|
||||
private IntegerBox numberOfLastRacesToShowBox;
|
||||
|
||||
private CheckBox showOverallLeaderboardOnSamePageCheckbox;
|
||||
|
||||
public LeaderboardSettingsDialogComponent(List<DetailType> maneuverDetailSelection,
|
||||
List<DetailType> legDetailSelection, List<DetailType> raceDetailSelection, List<DetailType> overallDetailSelection,
|
||||
List<RaceColumnDTO> raceAllRaceColumns, Iterable<RaceColumnDTO> raceColumnSelection, RaceColumnSelection raceColumnSelectionStrategy,
|
||||
boolean autoExpandPreSelectedRace, long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, StringMessages stringMessages) {
|
||||
List<DetailType> legDetailSelection, List<DetailType> raceDetailSelection,
|
||||
List<DetailType> overallDetailSelection, List<RaceColumnDTO> raceAllRaceColumns,
|
||||
Iterable<RaceColumnDTO> raceColumnSelection, RaceColumnSelection raceColumnSelectionStrategy,
|
||||
boolean autoExpandPreSelectedRace, boolean showOverallLeaderboardOnSamePage,
|
||||
long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, StringMessages stringMessages) {
|
||||
this.raceAllRaceColumns = raceAllRaceColumns;
|
||||
this.numberOfLastRacesToShow = raceColumnSelectionStrategy.getNumberOfLastRaceColumnsToShow();
|
||||
this.activeRaceColumnSelectionStrategy = raceColumnSelectionStrategy.getType();
|
||||
@@ -75,12 +79,14 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
this.autoExpandPreSelectedRace = autoExpandPreSelectedRace;
|
||||
this.delayBetweenAutoAdvancesInMilliseconds = delayBetweenAutoAdvancesInMilliseconds;
|
||||
this.delayInMilliseconds = delayInMilliseconds;
|
||||
this.showOverallLeaderboardOnSamePage = showOverallLeaderboardOnSamePage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
|
||||
FlowPanel dialogPanel = new FlowPanel();
|
||||
dialogPanel.add(createSelectedRacesPanel(dialog));
|
||||
dialogPanel.add(createOverallLeaderboardSelectionPanel(dialog));
|
||||
dialogPanel.add(createOverallDetailPanel(dialog));
|
||||
dialogPanel.add(createRaceDetailPanel(dialog));
|
||||
dialogPanel.add(createLegDetailsPanel(dialog));
|
||||
@@ -274,6 +280,19 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
return selectedRacesPanel;
|
||||
}
|
||||
|
||||
private FlowPanel createOverallLeaderboardSelectionPanel(DataEntryDialog<?> dialog) {
|
||||
FlowPanel overallLeaderboardSelectionPanel = new FlowPanel();
|
||||
overallLeaderboardSelectionPanel.addStyleName("SettingsDialogComponent");
|
||||
overallLeaderboardSelectionPanel.add(dialog.createHeadline(stringMessages.overallLeaderboardSelection(), true));
|
||||
FlowPanel overallLeaderboardSelectionContent = new FlowPanel();
|
||||
overallLeaderboardSelectionContent.addStyleName("dialogInnerContent");
|
||||
overallLeaderboardSelectionPanel.add(overallLeaderboardSelectionContent);
|
||||
showOverallLeaderboardOnSamePageCheckbox = dialog.createCheckbox(stringMessages.showOverallLeaderboardOnSamePage());
|
||||
showOverallLeaderboardOnSamePageCheckbox.setValue(showOverallLeaderboardOnSamePage);
|
||||
overallLeaderboardSelectionContent.add(showOverallLeaderboardOnSamePageCheckbox);
|
||||
return overallLeaderboardSelectionPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LeaderboardSettings getResult() {
|
||||
List<DetailType> maneuverDetailsToShow = new ArrayList<DetailType>();
|
||||
@@ -318,7 +337,8 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone
|
||||
lastNRacesToShowValue,
|
||||
autoExpandPreSelectedRace, 1000l * (delayBetweenAutoAdvancesValue == null ? 0l : delayBetweenAutoAdvancesValue.longValue()),
|
||||
1000 * (delayInSecondsValue == null ? 0 : delayInSecondsValue.longValue()),
|
||||
null, true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy);
|
||||
null, true, /* updateUponPlayStateChange */ true, activeRaceColumnSelectionStrategy,
|
||||
showOverallLeaderboardOnSamePageCheckbox.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-11
@@ -39,7 +39,7 @@ public class LeaderboardSettingsFactory {
|
||||
* this argument
|
||||
*/
|
||||
public LeaderboardSettings createNewSettingsForPlayMode(PlayModes playMode, String nameOfRaceToSort, String nameOfRaceColumnToShow,
|
||||
String nameOfRaceToShow, RaceColumnSelection raceColumnSelection) {
|
||||
String nameOfRaceToShow, RaceColumnSelection raceColumnSelection, boolean showOverallLeaderboardsOnSamePage) {
|
||||
if (nameOfRaceColumnToShow != null && nameOfRaceToShow != null) {
|
||||
throw new IllegalArgumentException("Can identify only one race to show, either by race name or by its column name, but not both");
|
||||
}
|
||||
@@ -70,11 +70,11 @@ public class LeaderboardSettingsFactory {
|
||||
/* set autoExpandPreSelectedRace to true if we look at a single race */ nameOfRaceColumnToShow != null || nameOfRaceToShow != null,
|
||||
/* refresh interval */ null, /* delay to live */ null,
|
||||
/* name of race to sort */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true,
|
||||
raceColumnSelection.getType());
|
||||
raceColumnSelection.getType(), showOverallLeaderboardsOnSamePage);
|
||||
break;
|
||||
case Replay:
|
||||
settings = createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, nameOfRaceToSort, /* autoExpandFirstRace */
|
||||
nameOfRaceColumnToShow != null);
|
||||
nameOfRaceColumnToShow != null, showOverallLeaderboardsOnSamePage);
|
||||
break;
|
||||
}
|
||||
return settings;
|
||||
@@ -89,28 +89,33 @@ public class LeaderboardSettingsFactory {
|
||||
* @param namesOfRacesToShow
|
||||
* alternatively, races to show can also be specified by their race names; if not <code>null</code>,
|
||||
* <code>namesOfRaceColumnsToShow</code> must be <code>null</code>
|
||||
* @param showOverallLeaderboardsOnSamePage
|
||||
* tells whether a meta leaderboard should be shown beneath the actual leaderboard on the same page, if
|
||||
* such a meta leaderboard exists; if multiple such meta leaderboards exist, they are all shown
|
||||
*/
|
||||
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace) {
|
||||
List<String> namesOfRacesToShow, String nameOfRaceToSort, boolean autoExpandPreSelectedRace, boolean showOverallLeaderboardsOnSamePage) {
|
||||
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, /* leave overallDetailsToShow unchanged */ null,
|
||||
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null);
|
||||
nameOfRaceToSort, autoExpandPreSelectedRace, /* refreshIntervalMillis */ null, showOverallLeaderboardsOnSamePage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #createNewDefaultSettings(List, List, String, boolean)}, only that an additional refresh interval for auto-refresh
|
||||
* Like {@link #createNewDefaultSettings(List, List, String, boolean, boolean)}, only that an additional refresh interval for auto-refresh
|
||||
* may be specified; if <code>null</code>, no auto-refresh shall be performed
|
||||
*/
|
||||
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
|
||||
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis) {
|
||||
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetailsToShow, nameOfRaceToSort, autoExpandPreSelectedRace, refreshIntervalMillis,
|
||||
/* numberOfLastRacesToShow */ null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT);
|
||||
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, boolean showOverallLeaderboardsOnSamePage) {
|
||||
return createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, overallDetailsToShow,
|
||||
nameOfRaceToSort, autoExpandPreSelectedRace, refreshIntervalMillis,
|
||||
/* numberOfLastRacesToShow */null, /* raceColumnSelectionStrategy */ RaceColumnSelectionStrategies.EXPLICIT,
|
||||
showOverallLeaderboardsOnSamePage);
|
||||
}
|
||||
|
||||
public LeaderboardSettings createNewDefaultSettings(List<String> namesOfRaceColumnsToShow,
|
||||
List<String> namesOfRacesToShow, List<DetailType> overallDetailsToShow, String nameOfRaceToSort,
|
||||
boolean autoExpandPreSelectedRace, Long refreshIntervalMillis, Integer numberOfLastRacesToShow,
|
||||
RaceColumnSelectionStrategies raceColumnSelectionStrategy) {
|
||||
RaceColumnSelectionStrategies raceColumnSelectionStrategy, boolean showOverallLeaderboardsOnSamePage) {
|
||||
if (namesOfRaceColumnsToShow != null && namesOfRacesToShow != null) {
|
||||
throw new IllegalArgumentException("Can specify race columns either by column or by race name, not both");
|
||||
}
|
||||
@@ -129,6 +134,6 @@ public class LeaderboardSettingsFactory {
|
||||
namesOfRacesToShow, numberOfLastRacesToShow,
|
||||
autoExpandPreSelectedRace, refreshIntervalMillis, /* delay to live */ null,
|
||||
/* sort by column */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true,
|
||||
raceColumnSelectionStrategy);
|
||||
raceColumnSelectionStrategy, showOverallLeaderboardsOnSamePage);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -91,14 +91,14 @@ public class LeaderboardUrlConfigurationDialog extends SettingsDialog<Leaderboar
|
||||
}
|
||||
LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
namesOfRaceColumnsToShow, /* namesOfRacesToShow */null, /* nameOfRaceToSort */null, /* autoExpandPreSelectedRace */
|
||||
false);
|
||||
false, /* showMetaLeaderboardsOnSamePage */ false);
|
||||
List<DetailType> overallDetailsToShow = Collections.emptyList();
|
||||
leaderboardSettingsDialogComponent = new LeaderboardSettingsDialogComponent(settings.getManeuverDetailsToShow(),
|
||||
settings.getLegDetailsToShow(), settings.getRaceDetailsToShow(), overallDetailsToShow, raceList,
|
||||
/* select all races by default */ raceList, new ExplicitRaceColumnSelection(),
|
||||
/* autoExpandPreSelectedRace */ false,
|
||||
/* delayBetweenAutoAdvancesInMilliseconds */ 3000l,
|
||||
/* delayInMilliseconds */ 3000l, stringMessages);
|
||||
/* showOverallLeaderboardOnSamePage */ false,
|
||||
/* delayBetweenAutoAdvancesInMilliseconds */ 3000l, /* delayInMilliseconds */ 3000l, stringMessages);
|
||||
}
|
||||
|
||||
private void updateURL(LeaderboardUrlSettings settings, String leaderboardName) {
|
||||
|
||||
+25
-1
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.InvertibleComparator;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.SortingOrder;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField;
|
||||
@@ -94,6 +95,20 @@ public class LegColumn extends ExpandableSortableColumn<String> {
|
||||
}
|
||||
}
|
||||
|
||||
private class GapChangeSinceLegStartInSeconds extends AbstractLegDetailField<Double> {
|
||||
@Override
|
||||
protected Double getFromNonNullEntry(LegEntryDTO entry) {
|
||||
return entry.gapChangeSinceLegStartInSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
private class SideToWhichMarkAtLegStartWasRounded extends AbstractLegDetailField<Tack> {
|
||||
@Override
|
||||
protected Tack getFromNonNullEntry(LegEntryDTO entry) {
|
||||
return entry.sideToWhichMarkAtLegStartWasRounded;
|
||||
}
|
||||
}
|
||||
|
||||
private class VelocityMadeGoodInKnots extends AbstractLegDetailField<Double> {
|
||||
@Override
|
||||
protected Double getFromNonNullEntry(LegEntryDTO entry) {
|
||||
@@ -212,7 +227,8 @@ public class LegColumn extends ExpandableSortableColumn<String> {
|
||||
|
||||
public static DetailType[] getAvailableLegDetailColumnTypes() {
|
||||
return new DetailType[] { DetailType.AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.DISTANCE_TRAVELED,
|
||||
DetailType.GAP_TO_LEADER_IN_SECONDS, DetailType.CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
DetailType.GAP_TO_LEADER_IN_SECONDS, DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS,
|
||||
DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED, DetailType.CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
DetailType.WINDWARD_DISTANCE_TO_GO_IN_METERS, DetailType.NUMBER_OF_MANEUVERS,
|
||||
DetailType.ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS, DetailType.VELOCITY_MADE_GOOD_IN_KNOTS,
|
||||
DetailType.TIME_TRAVELED, DetailType.AVERAGE_CROSS_TRACK_ERROR_IN_METERS, DetailType.RANK_GAIN };
|
||||
@@ -247,6 +263,14 @@ public class LegColumn extends ExpandableSortableColumn<String> {
|
||||
new GapToLeaderInSeconds(), DetailType.GAP_TO_LEADER_IN_SECONDS.getPrecision(),
|
||||
DetailType.GAP_TO_LEADER_IN_SECONDS.getDefaultSortingOrder(),
|
||||
detailHeaderStyle, detailColumnStyle));
|
||||
result.put(DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS,
|
||||
new FormattedDoubleLegDetailColumn(stringMessages.gapChangeSinceLegStartInSeconds(), "["+stringMessages.gapToLeaderInSecondsUnit()+"]",
|
||||
new GapChangeSinceLegStartInSeconds(), DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS.getPrecision(),
|
||||
DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS.getDefaultSortingOrder(),
|
||||
detailHeaderStyle, detailColumnStyle));
|
||||
result.put(DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED,
|
||||
new SideToWhichMarkAtLegStartWasRoundedColumn(stringMessages.sideToWhichMarkAtLegStartWasRounded(),
|
||||
new SideToWhichMarkAtLegStartWasRounded(), detailHeaderStyle, detailColumnStyle));
|
||||
result.put(DetailType.VELOCITY_MADE_GOOD_IN_KNOTS,
|
||||
new FormattedDoubleLegDetailColumn(stringMessages.velocityMadeGoodInKnots(),
|
||||
"["+stringMessages.velocityMadeGoodInKnotsUnit()+"]", new VelocityMadeGoodInKnots(),
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.gwt.ui.leaderboard;
|
||||
|
||||
import com.google.gwt.cell.client.TextCell;
|
||||
import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
|
||||
|
||||
public class SideToWhichMarkAtLegStartWasRoundedColumn extends LegDetailColumn<Tack, String> {
|
||||
public SideToWhichMarkAtLegStartWasRoundedColumn(String title, LegDetailField<Tack> field, String headerStyle, String columnStyle) {
|
||||
super(title, null, field, new TextCell(), DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED.getDefaultSortingOrder(),
|
||||
headerStyle, columnStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(LeaderboardRowDTO row) {
|
||||
return getField().get(row) == null ? null : getField().get(row).name();
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -461,9 +461,10 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
|
||||
String leaderboardName, String leaderboardGroupName, final ErrorReporter errorReporter,
|
||||
final StringMessages stringMessages, UserAgentDetails userAgent) {
|
||||
super(sailingService, asyncActionsExecutor, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */false),
|
||||
/* racesToShow */ null, /* namesOfRacesToShow */ null, null, /* autoExpandFirstRace */ false,
|
||||
/* showMetaLeaderboardsOnSamePage */ false),
|
||||
new CompetitorSelectionModel(/* hasMultiSelection */true),
|
||||
leaderboardName, leaderboardGroupName, errorReporter, stringMessages, userAgent, /* showRaceDetails */ true);
|
||||
leaderboardName, errorReporter, stringMessages, userAgent, /* showRaceDetails */ true);
|
||||
suppressedCompetitorsShown = new ListDataProvider<CompetitorDTO>(new ArrayList<CompetitorDTO>());
|
||||
suppressedCompetitorsTable = createSuppressedCompetitorsTable();
|
||||
ImageResource importIcon = resources.importIcon();
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ public class LeaderboardEditPage extends AbstractEntryPoint {
|
||||
public void onSuccess(List<String> leaderboardNames) {
|
||||
String leaderboardName = Window.Location.getParameter("name");
|
||||
if (leaderboardNames.contains(leaderboardName)) {
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages.editScores(), stringMessages);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(stringMessages.editScores(), stringMessages, LeaderboardEditPage.this);
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
EditableLeaderboardPanel leaderboardPanel = new EditableLeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardName, null,
|
||||
LeaderboardEditPage.this, stringMessages, userAgent);
|
||||
|
||||
+1
-5
@@ -23,16 +23,12 @@ public class GlobalNavigationPanel extends FlowPanel {
|
||||
|
||||
public GlobalNavigationPanel(StringMessages stringMessages, boolean showHomeNavigation, String leaderboardName, String leaderboardGroupName) {
|
||||
super();
|
||||
|
||||
setStyleName("globalNavigation");
|
||||
|
||||
debugParam = Window.Location.getParameter("gwt.codesvr");
|
||||
|
||||
String spectatorViewLink = "/gwt/Spectator.html";
|
||||
String leaderboardViewLink = "/gwt/Leaderboard.html";
|
||||
String homeLink = "/";
|
||||
|
||||
if(showHomeNavigation) {
|
||||
if (showHomeNavigation) {
|
||||
if (leaderboardGroupName != null && !leaderboardGroupName.isEmpty()) {
|
||||
String leaderBoardGroupLink = spectatorViewLink + "?showRaceDetails=true&leaderboardGroupName=" + leaderboardGroupName;
|
||||
addNavigationLink(leaderboardGroupName, leaderBoardGroupLink, "leaderBoardGroup", "Go to the Event overview.");
|
||||
|
||||
+17
-5
@@ -36,6 +36,8 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
||||
private String leaderboardGroupName;
|
||||
private RaceBoardViewModes viewMode;
|
||||
|
||||
private GlobalNavigationPanel globalNavigationPanel;
|
||||
|
||||
@Override
|
||||
public void onModuleLoad() {
|
||||
super.onModuleLoad();
|
||||
@@ -172,12 +174,22 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
||||
}
|
||||
|
||||
private FlowPanel createLogoAndTitlePanel(RaceBoardPanel raceBoardPanel) {
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(regattaName, selectedRace.name, stringMessages);
|
||||
globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, leaderboardName, leaderboardGroupName);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(regattaName, selectedRace.name, stringMessages, this) {
|
||||
@Override
|
||||
public void onResize() {
|
||||
super.onResize();
|
||||
if (isSmallWidth()) {
|
||||
remove(globalNavigationPanel);
|
||||
} else {
|
||||
add(globalNavigationPanel);
|
||||
}
|
||||
}
|
||||
};
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
|
||||
FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, leaderboardName, leaderboardGroupName);
|
||||
logoAndTitlePanel.add(globalNavigationPanel);
|
||||
|
||||
if (!isSmallWidth()) {
|
||||
logoAndTitlePanel.add(globalNavigationPanel);
|
||||
}
|
||||
return logoAndTitlePanel;
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -144,6 +144,7 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
|
||||
private void createOneScreenView(String leaderboardName, String leaderboardGroupName, FlowPanel mainPanel) {
|
||||
// create the default leaderboard and select the right race
|
||||
leaderboardPanel = createLeaderboardPanel(leaderboardName, leaderboardGroupName);
|
||||
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
|
||||
RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelectionModel, stringMessages);
|
||||
raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceMap);
|
||||
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
|
||||
@@ -186,10 +187,11 @@ public class RaceBoardPanel extends FormPanel implements RegattaDisplayer, RaceS
|
||||
.createNewSettingsForPlayMode(timer.getPlayMode(),
|
||||
/* nameOfRaceToSort */ selectedRaceIdentifier.getRaceName(),
|
||||
/* nameOfRaceColumnToShow */ null, /* nameOfRaceToShow */ selectedRaceIdentifier.getRaceName(),
|
||||
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier));
|
||||
new ExplicitRaceColumnSelectionWithPreselectedRace(selectedRaceIdentifier),
|
||||
/* showOverallLeaderboardsOnSamePage */ false);
|
||||
return new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderBoardSettings, selectedRaceIdentifier,
|
||||
competitorSelectionModel, timer, leaderboardName, leaderboardGroupName, errorReporter, stringMessages,
|
||||
userAgent, /* showRaceDetails */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false);
|
||||
competitorSelectionModel, timer, leaderboardName, errorReporter, stringMessages, userAgent,
|
||||
/* showRaceDetails */ true, raceTimesInfoProvider, /* autoExpandLastRaceColumn */ false);
|
||||
}
|
||||
|
||||
private <SettingsType> void addComponentToNavigationMenu(final ComponentViewer componentViewer,
|
||||
|
||||
+133
-33
@@ -36,7 +36,6 @@ import org.osgi.framework.BundleContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.ControlPoint;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
@@ -44,6 +43,7 @@ import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.Gate;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
@@ -108,6 +108,7 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.RacesHandle;
|
||||
@@ -128,9 +129,9 @@ import com.sap.sailing.geocoding.ReverseGeocoder;
|
||||
import com.sap.sailing.gwt.ui.client.SailingService;
|
||||
import com.sap.sailing.gwt.ui.shared.BoatClassDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompetitorRaceDataDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CourseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
@@ -144,15 +145,16 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LegEntryDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkpassingManeuverDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.PlacemarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.PlacemarkOrderDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.PositionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceCourseMarksDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceColumnDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceColumnInSeriesDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceCourseMarksDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceEventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceEventLogDTO;
|
||||
@@ -832,6 +834,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
result.finished = trackedLeg.hasFinishedLeg(timePoint);
|
||||
result.gapToLeaderInSeconds = trackedLeg.getGapToLeaderInSeconds(timePoint,
|
||||
legRanksCache.get(trackedLeg.getLeg()).entrySet().iterator().next().getKey());
|
||||
if (result.gapToLeaderInSeconds != null) {
|
||||
// FIXME problem: asking just after the beginning of the leg yields very different values from asking for the end of the previous leg.
|
||||
// This is because for the previous leg it's decided based on the mark passings; for the next (current) leg it's decided based on
|
||||
// windward distance and VMG
|
||||
Double gapAtEndOfPreviousLegInSeconds = getGapAtEndOfPreviousLegInSeconds(trackedLeg);
|
||||
if (gapAtEndOfPreviousLegInSeconds != null) {
|
||||
result.gapChangeSinceLegStartInSeconds = result.gapToLeaderInSeconds - gapAtEndOfPreviousLegInSeconds;
|
||||
}
|
||||
}
|
||||
LinkedHashMap<Competitor, Integer> legRanks = legRanksCache.get(trackedLeg.getLeg());
|
||||
if (legRanks != null) {
|
||||
result.rank = legRanks.get(trackedLeg.getCompetitor());
|
||||
@@ -849,34 +860,58 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
Distance windwardDistanceToGo = trackedLeg.getWindwardDistanceToGo(timePoint);
|
||||
result.windwardDistanceToGoInMeters = windwardDistanceToGo == null ? null : windwardDistanceToGo
|
||||
.getMeters();
|
||||
List<Maneuver> maneuvers = trackedLeg.getManeuvers(timePoint, waitForLatestAnalyses);
|
||||
if (maneuvers != null) {
|
||||
result.numberOfManeuvers = new HashMap<ManeuverType, Integer>();
|
||||
result.numberOfManeuvers.put(ManeuverType.TACK, 0);
|
||||
result.numberOfManeuvers.put(ManeuverType.JIBE, 0);
|
||||
result.numberOfManeuvers.put(ManeuverType.PENALTY_CIRCLE, 0);
|
||||
Map<ManeuverType, Double> totalManeuverLossInMeters = new HashMap<ManeuverType, Double>();
|
||||
totalManeuverLossInMeters.put(ManeuverType.TACK, 0.0);
|
||||
totalManeuverLossInMeters.put(ManeuverType.JIBE, 0.0);
|
||||
totalManeuverLossInMeters.put(ManeuverType.PENALTY_CIRCLE, 0.0);
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
switch (maneuver.getType()) {
|
||||
case TACK:
|
||||
case JIBE:
|
||||
case PENALTY_CIRCLE:
|
||||
if (maneuver.getManeuverLoss() != null) {
|
||||
result.numberOfManeuvers.put(maneuver.getType(), result.numberOfManeuvers.get(maneuver.getType())+1);
|
||||
totalManeuverLossInMeters.put(maneuver.getType(),
|
||||
totalManeuverLossInMeters.get(maneuver.getType()) + maneuver.getManeuverLoss().getMeters());
|
||||
final TimePoint startOfRace = trackedLeg.getTrackedLeg().getTrackedRace().getStartOfRace();
|
||||
if (startOfRace != null && trackedLeg.hasStartedLeg(timePoint)) {
|
||||
// not using trackedLeg.getManeuvers(...) because it may not catch the mark passing maneuver starting this leg
|
||||
// because that may have been detected as slightly before the mark passing time, hence associated with the previous leg
|
||||
List<Maneuver> maneuvers = trackedLeg.getTrackedLeg().getTrackedRace()
|
||||
.getManeuvers(trackedLeg.getCompetitor(), startOfRace, timePoint, waitForLatestAnalyses);
|
||||
if (maneuvers != null) {
|
||||
result.numberOfManeuvers = new HashMap<ManeuverType, Integer>();
|
||||
result.numberOfManeuvers.put(ManeuverType.TACK, 0);
|
||||
result.numberOfManeuvers.put(ManeuverType.JIBE, 0);
|
||||
result.numberOfManeuvers.put(ManeuverType.PENALTY_CIRCLE, 0);
|
||||
Map<ManeuverType, Double> totalManeuverLossInMeters = new HashMap<ManeuverType, Double>();
|
||||
totalManeuverLossInMeters.put(ManeuverType.TACK, 0.0);
|
||||
totalManeuverLossInMeters.put(ManeuverType.JIBE, 0.0);
|
||||
totalManeuverLossInMeters.put(ManeuverType.PENALTY_CIRCLE, 0.0);
|
||||
TimePoint startOfLeg = trackedLeg.getStartTime();
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
// don't count maneuvers that were in previous legs
|
||||
switch (maneuver.getType()) {
|
||||
case TACK:
|
||||
case JIBE:
|
||||
case PENALTY_CIRCLE:
|
||||
if (!maneuver.getTimePoint().before(startOfLeg) && (!trackedLeg.hasFinishedLeg(timePoint) ||
|
||||
maneuver.getTimePoint().before(trackedLeg.getFinishTime()))) {
|
||||
if (maneuver.getManeuverLoss() != null) {
|
||||
result.numberOfManeuvers.put(maneuver.getType(),
|
||||
result.numberOfManeuvers.get(maneuver.getType()) + 1);
|
||||
totalManeuverLossInMeters.put(maneuver.getType(),
|
||||
totalManeuverLossInMeters.get(maneuver.getType())
|
||||
+ maneuver.getManeuverLoss().getMeters());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MARK_PASSING:
|
||||
// analyze all mark passings, not only those after this leg's start, to catch the mark passing
|
||||
// maneuver starting this leg, even if its time point is slightly before the mark passing starting this leg
|
||||
MarkPassingManeuver mpm = (MarkPassingManeuver) maneuver;
|
||||
if (mpm.getWaypointPassed() == trackedLeg.getLeg().getFrom()) {
|
||||
result.sideToWhichMarkAtLegStartWasRounded = mpm.getSide();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.averageManeuverLossInMeters = new HashMap<ManeuverType, Double>();
|
||||
for (ManeuverType maneuverType : new ManeuverType[] { ManeuverType.TACK, ManeuverType.JIBE, ManeuverType.PENALTY_CIRCLE }) {
|
||||
if (result.numberOfManeuvers.get(maneuverType) != 0) {
|
||||
result.averageManeuverLossInMeters.put(maneuverType,
|
||||
totalManeuverLossInMeters.get(maneuverType)/result.numberOfManeuvers.get(maneuverType));
|
||||
result.averageManeuverLossInMeters = new HashMap<ManeuverType, Double>();
|
||||
for (ManeuverType maneuverType : new ManeuverType[] { ManeuverType.TACK, ManeuverType.JIBE,
|
||||
ManeuverType.PENALTY_CIRCLE }) {
|
||||
if (result.numberOfManeuvers.get(maneuverType) != 0) {
|
||||
result.averageManeuverLossInMeters.put(
|
||||
maneuverType,
|
||||
totalManeuverLossInMeters.get(maneuverType)
|
||||
/ result.numberOfManeuvers.get(maneuverType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -884,6 +919,40 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
return result;
|
||||
}
|
||||
|
||||
private Double getGapAtEndOfPreviousLegInSeconds(TrackedLegOfCompetitor trackedLeg) throws NoWindException {
|
||||
final Double result;
|
||||
final Course course = trackedLeg.getTrackedLeg().getTrackedRace().getRace().getCourse();
|
||||
course.lockForRead();
|
||||
try {
|
||||
int indexOfStartWaypoint = course.getIndexOfWaypoint(trackedLeg.getLeg().getFrom());
|
||||
if (indexOfStartWaypoint == 0) {
|
||||
// trackedLeg was the first leg; gap is determined by gap of start line passing time points
|
||||
Iterable<MarkPassing> markPassingsForLegStart = trackedLeg.getTrackedLeg().getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getFrom());
|
||||
final Iterator<MarkPassing> markPassingsIter = markPassingsForLegStart.iterator();
|
||||
if (markPassingsIter.hasNext()) {
|
||||
TimePoint firstStart = markPassingsIter.next().getTimePoint();
|
||||
final MarkPassing markPassingForFrom = trackedLeg.getTrackedLeg().getTrackedRace().
|
||||
getMarkPassing(trackedLeg.getCompetitor(), trackedLeg.getLeg().getFrom());
|
||||
if (markPassingForFrom != null) {
|
||||
TimePoint competitorStart = markPassingForFrom.getTimePoint();
|
||||
result = (double) (competitorStart.asMillis() - firstStart.asMillis()) / 1000.;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
} else {
|
||||
TrackedLeg previousTrackedLeg = trackedLeg.getTrackedLeg().getTrackedRace().getTrackedLeg(course.getLegs().get(indexOfStartWaypoint-1));
|
||||
TrackedLegOfCompetitor previousTrackedLegOfCompetitor = previousTrackedLeg.getTrackedLeg(trackedLeg.getCompetitor());
|
||||
result = previousTrackedLegOfCompetitor.getGapToLeaderInSeconds(previousTrackedLegOfCompetitor.getFinishTime());
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
course.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegattaDTO> getRegattas() throws IllegalArgumentException {
|
||||
List<RegattaDTO> result = new ArrayList<RegattaDTO>();
|
||||
@@ -2163,7 +2232,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
@Override
|
||||
public Void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity, String comment) {
|
||||
return getService().apply(
|
||||
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName, new MillisecondsTimePoint(timePointOfLastCorrectionValidity), comment));
|
||||
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName,
|
||||
timePointOfLastCorrectionValidity == null ? null : new MillisecondsTimePoint(timePointOfLastCorrectionValidity),
|
||||
comment));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2460,12 +2531,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
private List<ManeuverDTO> createManeuverDTOsForCompetitor(List<Maneuver> maneuvers, TrackedRace trackedRace, Competitor competitor) {
|
||||
List<ManeuverDTO> result = new ArrayList<ManeuverDTO>();
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
ManeuverDTO maneuverDTO = new ManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
|
||||
final ManeuverDTO maneuverDTO;
|
||||
if (maneuver.getType() == ManeuverType.MARK_PASSING) {
|
||||
maneuverDTO = new MarkpassingManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
|
||||
new PositionDTO(maneuver.getPosition().getLatDeg(), maneuver.getPosition().getLngDeg()),
|
||||
maneuver.getTimePoint().asDate(),
|
||||
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()),
|
||||
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()),
|
||||
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters());
|
||||
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters(),
|
||||
((MarkPassingManeuver) maneuver).getSide());
|
||||
} else {
|
||||
maneuverDTO = new ManeuverDTO(maneuver.getType(), maneuver.getNewTack(),
|
||||
new PositionDTO(maneuver.getPosition().getLatDeg(), maneuver.getPosition().getLngDeg()),
|
||||
maneuver.getTimePoint().asDate(),
|
||||
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()),
|
||||
createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()),
|
||||
maneuver.getDirectionChangeInDegrees(), maneuver.getManeuverLoss()==null?null:maneuver.getManeuverLoss().getMeters());
|
||||
}
|
||||
result.add(maneuverDTO);
|
||||
}
|
||||
return result;
|
||||
@@ -2839,6 +2921,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
List<CompetitorDTO> competitorList = getCompetitorDTOList(preResult.get(raceColumn));
|
||||
result.add(new Pair<String, List<CompetitorDTO>>(raceColumn.getName(), competitorList));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getOverallLeaderboardNamesContaining(String leaderboardName) {
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard == null) {
|
||||
throw new IllegalArgumentException("Couldn't find leaderboard named "+leaderboardName);
|
||||
}
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (Map.Entry<String, Leaderboard> leaderboardEntry : getService().getLeaderboards().entrySet()) {
|
||||
if (leaderboardEntry.getValue() instanceof MetaLeaderboard) {
|
||||
MetaLeaderboard metaLeaderboard = (MetaLeaderboard) leaderboardEntry.getValue();
|
||||
if (Util.contains(metaLeaderboard.getLeaderboards(), leaderboard)) {
|
||||
result.add(leaderboardEntry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+14
-1
@@ -11,6 +11,7 @@ import java.util.Set;
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
|
||||
public abstract class AbstractLeaderboardDTO implements IsSerializable {
|
||||
public String name;
|
||||
@@ -261,7 +262,7 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
|
||||
/**
|
||||
* @return <code>true</code> if the leaderboard contains a race which is live
|
||||
*/
|
||||
public boolean containsLiveRace() {
|
||||
public boolean hasLiveRace() {
|
||||
for (RaceColumnDTO race : getRaceList()) {
|
||||
for (FleetDTO fleet : race.getFleets()) {
|
||||
if (race.isLive(fleet)) {
|
||||
@@ -272,6 +273,18 @@ public abstract class AbstractLeaderboardDTO implements IsSerializable {
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Pair<RaceColumnDTO, FleetDTO>> getLiveRaces() {
|
||||
List<Pair<RaceColumnDTO, FleetDTO>> result = new ArrayList<Pair<RaceColumnDTO, FleetDTO>>();
|
||||
for (RaceColumnDTO race : getRaceList()) {
|
||||
for (FleetDTO fleet : race.getFleets()) {
|
||||
if (race.isLive(fleet)) {
|
||||
result.add(new Pair<RaceColumnDTO, FleetDTO>(race, fleet));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
||||
+2
-2
@@ -130,9 +130,9 @@ public class LeaderboardGroupDTO extends NamedDTO implements IsSerializable {
|
||||
/**
|
||||
* @return <code>true</code> if the group contains a race which is live.
|
||||
*/
|
||||
public boolean containsLiveRace() {
|
||||
public boolean hasLiveRace() {
|
||||
for (StrippedLeaderboardDTO leaderboard : leaderboards) {
|
||||
if (leaderboard.containsLiveRace()) {
|
||||
if (leaderboard.hasLiveRace()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+31
-5
@@ -4,6 +4,7 @@ import java.util.Map;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
|
||||
/**
|
||||
* Holds data about one competitor's performance in one leg of one race represented in the
|
||||
@@ -18,6 +19,8 @@ public class LegEntryDTO implements IsSerializable {
|
||||
public Double averageSpeedOverGroundInKnots;
|
||||
public int rank;
|
||||
public Double gapToLeaderInSeconds;
|
||||
public Double gapChangeSinceLegStartInSeconds;
|
||||
public Tack sideToWhichMarkAtLegStartWasRounded;
|
||||
public Double estimatedTimeToNextWaypointInSeconds;
|
||||
|
||||
/**
|
||||
@@ -41,11 +44,13 @@ public class LegEntryDTO implements IsSerializable {
|
||||
public Map<ManeuverType, Integer> numberOfManeuvers;
|
||||
public Map<ManeuverType, Double> averageManeuverLossInMeters;
|
||||
public Double averageCrossTrackErrorInMeters;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result
|
||||
+ ((averageCrossTrackErrorInMeters == null) ? 0 : averageCrossTrackErrorInMeters.hashCode());
|
||||
result = prime * result + ((averageManeuverLossInMeters == null) ? 0 : averageManeuverLossInMeters.hashCode());
|
||||
result = prime * result
|
||||
+ ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode());
|
||||
result = prime * result
|
||||
@@ -55,11 +60,14 @@ public class LegEntryDTO implements IsSerializable {
|
||||
* result
|
||||
+ ((estimatedTimeToNextWaypointInSeconds == null) ? 0 : estimatedTimeToNextWaypointInSeconds.hashCode());
|
||||
result = prime * result + (finished ? 1231 : 1237);
|
||||
result = prime * result + ((gapChangeSinceLegStartInSeconds == null) ? 0 : gapChangeSinceLegStartInSeconds.hashCode());
|
||||
result = prime * result + ((gapToLeaderInSeconds == null) ? 0 : gapToLeaderInSeconds.hashCode());
|
||||
result = prime * result + ((numberOfManeuvers == null) ? 0 : numberOfManeuvers.hashCode());
|
||||
result = prime * result + rank;
|
||||
result = prime * result
|
||||
+ ((sideToWhichMarkAtLegStartWasRounded == null) ? 0 : sideToWhichMarkAtLegStartWasRounded.hashCode());
|
||||
result = prime * result + (started ? 1231 : 1237);
|
||||
result = prime * result + (int) (timeInMilliseconds ^ (timeInMilliseconds >>> 32));
|
||||
result = prime * result + ((timeInMilliseconds == null) ? 0 : timeInMilliseconds.hashCode());
|
||||
result = prime * result + ((velocityMadeGoodInKnots == null) ? 0 : velocityMadeGoodInKnots.hashCode());
|
||||
result = prime * result
|
||||
+ ((windwardDistanceToGoInMeters == null) ? 0 : windwardDistanceToGoInMeters.hashCode());
|
||||
@@ -74,6 +82,16 @@ public class LegEntryDTO implements IsSerializable {
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
LegEntryDTO other = (LegEntryDTO) obj;
|
||||
if (averageCrossTrackErrorInMeters == null) {
|
||||
if (other.averageCrossTrackErrorInMeters != null)
|
||||
return false;
|
||||
} else if (!averageCrossTrackErrorInMeters.equals(other.averageCrossTrackErrorInMeters))
|
||||
return false;
|
||||
if (averageManeuverLossInMeters == null) {
|
||||
if (other.averageManeuverLossInMeters != null)
|
||||
return false;
|
||||
} else if (!averageManeuverLossInMeters.equals(other.averageManeuverLossInMeters))
|
||||
return false;
|
||||
if (averageSpeedOverGroundInKnots == null) {
|
||||
if (other.averageSpeedOverGroundInKnots != null)
|
||||
return false;
|
||||
@@ -96,6 +114,11 @@ public class LegEntryDTO implements IsSerializable {
|
||||
return false;
|
||||
if (finished != other.finished)
|
||||
return false;
|
||||
if (gapChangeSinceLegStartInSeconds == null) {
|
||||
if (other.gapChangeSinceLegStartInSeconds != null)
|
||||
return false;
|
||||
} else if (!gapChangeSinceLegStartInSeconds.equals(other.gapChangeSinceLegStartInSeconds))
|
||||
return false;
|
||||
if (gapToLeaderInSeconds == null) {
|
||||
if (other.gapToLeaderInSeconds != null)
|
||||
return false;
|
||||
@@ -108,9 +131,14 @@ public class LegEntryDTO implements IsSerializable {
|
||||
return false;
|
||||
if (rank != other.rank)
|
||||
return false;
|
||||
if (sideToWhichMarkAtLegStartWasRounded != other.sideToWhichMarkAtLegStartWasRounded)
|
||||
return false;
|
||||
if (started != other.started)
|
||||
return false;
|
||||
if (timeInMilliseconds != other.timeInMilliseconds)
|
||||
if (timeInMilliseconds == null) {
|
||||
if (other.timeInMilliseconds != null)
|
||||
return false;
|
||||
} else if (!timeInMilliseconds.equals(other.timeInMilliseconds))
|
||||
return false;
|
||||
if (velocityMadeGoodInKnots == null) {
|
||||
if (other.velocityMadeGoodInKnots != null)
|
||||
@@ -124,6 +152,4 @@ public class LegEntryDTO implements IsSerializable {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+23
-1
@@ -2,9 +2,13 @@ package com.sap.sailing.gwt.ui.shared;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.i18n.client.NumberFormat;
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
|
||||
public class ManeuverDTO implements IsSerializable {
|
||||
public ManeuverType type;
|
||||
@@ -37,5 +41,23 @@ public class ManeuverDTO implements IsSerializable {
|
||||
this.directionChangeInDegrees = directionChangeInDegrees;
|
||||
this.maneuverLossInMeters = maneuverLossInMeters;
|
||||
}
|
||||
|
||||
|
||||
public String toString(StringMessages stringMessages) {
|
||||
SpeedWithBearingDTO before = this.speedWithBearingBefore;
|
||||
SpeedWithBearingDTO after = this.speedWithBearingAfter;
|
||||
|
||||
String timeAndManeuver = DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(this.timepoint)
|
||||
+ ": " + this.type.name();
|
||||
String directionChange = stringMessages.directionChange() + ": "
|
||||
+ ((int) this.directionChangeInDegrees) + " "+stringMessages.degreesShort()+" ("
|
||||
+ ((int) before.bearingInDegrees) + " deg -> " + ((int) after.bearingInDegrees) + " "+stringMessages.degreesShort()+")";
|
||||
String speedChange = stringMessages.speedChange() + ": "
|
||||
+ NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" ("
|
||||
+ NumberFormat.getDecimalFormat().format(before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" -> "
|
||||
+ NumberFormat.getDecimalFormat().format(after.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+")";
|
||||
String maneuverLoss = this.maneuverLossInMeters == null ? "" : ("; "+stringMessages.maneuverLoss()+": "+
|
||||
NumberFormat.getDecimalFormat().format(this.maneuverLossInMeters)+"m");
|
||||
String maneuverTitle = timeAndManeuver + "; " + directionChange + "; " + speedChange + maneuverLoss;
|
||||
return maneuverTitle;
|
||||
}
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
package com.sap.sailing.gwt.ui.shared;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
|
||||
public class MarkpassingManeuverDTO extends ManeuverDTO {
|
||||
public Tack side;
|
||||
|
||||
MarkpassingManeuverDTO() {}
|
||||
|
||||
public MarkpassingManeuverDTO(ManeuverType type, Tack newTack, PositionDTO position, Date timepoint,
|
||||
SpeedWithBearingDTO speedWithBearingBefore, SpeedWithBearingDTO speedWithBearingAfter,
|
||||
double directionChangeInDegrees, Double maneuverLossInMeters, Tack side) {
|
||||
super(type, newTack, position, timepoint, speedWithBearingBefore, speedWithBearingAfter, directionChangeInDegrees,
|
||||
maneuverLossInMeters);
|
||||
this.side = side;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(StringMessages stringMessages) {
|
||||
StringBuilder result = new StringBuilder(super.toString(stringMessages));
|
||||
result.append(", ");
|
||||
result.append(stringMessages.passedTo(side.name()));
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -143,14 +143,13 @@ public class RaceColumnDTO extends NamedDTO implements IsSerializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fleet TODO
|
||||
* @return <code>true</code> if the startOfTracking is after the current date and there's no end of the race
|
||||
*/
|
||||
public boolean isLive(FleetDTO fleet) {
|
||||
boolean result = false;
|
||||
final long now = System.currentTimeMillis();
|
||||
if (getWhenLastTrackedRaceWasLive(fleet) != null
|
||||
&& getWhenLastTrackedRaceWasLive(fleet).getTime() > now - IS_LIVE_GRACE_PERIOD_IN_MILLIS) {
|
||||
Date whenLastTrackedRaceWasLive = getWhenLastTrackedRaceWasLive(fleet);
|
||||
if (whenLastTrackedRaceWasLive != null && whenLastTrackedRaceWasLive.getTime() > now - IS_LIVE_GRACE_PERIOD_IN_MILLIS) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
|
||||
+24
-31
@@ -96,13 +96,10 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
dataSeriesByCompetitor = new HashMap<CompetitorDTO, Series>();
|
||||
markPassingSeriesByCompetitor = new HashMap<CompetitorDTO, Series>();
|
||||
setSize("100%", "100%");
|
||||
|
||||
noCompetitorsSelectedLabel = new Label(stringMessages.selectAtLeastOneCompetitor() + ".");
|
||||
noCompetitorsSelectedLabel.setStyleName("abstractChartPanel-importantMessageOfChart");
|
||||
|
||||
chart = createChart();
|
||||
createChart();
|
||||
setSelectedDetailType(detailType);
|
||||
|
||||
competitorSelectionProvider.addCompetitorSelectionChangeListener(this);
|
||||
raceSelectionProvider.addRaceSelectionChangeListener(this);
|
||||
}
|
||||
@@ -124,7 +121,7 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
.setMarginRight(65)
|
||||
.setBorderColor(new Color("#CACACA"))
|
||||
.setBackgroundColor(new Color("#EBEBEB"))
|
||||
.setBorderWidth(1)
|
||||
.setBorderWidth(0)
|
||||
.setBorderRadius(0)
|
||||
.setPlotBorderWidth(0)
|
||||
.setCredits(new Credits().setEnabled(false))
|
||||
@@ -316,18 +313,7 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
newRaceDataPoints = new Point[currentPointIndex];
|
||||
System.arraycopy(raceDataPointsToAdd, 0, newRaceDataPoints, 0, currentPointIndex);
|
||||
}
|
||||
|
||||
if(timeRangeWithZoomProvider.isZoomed()) {
|
||||
Pair<Date, Date> timeZoom = timeRangeWithZoomProvider.getTimeZoom();
|
||||
resetMinMaxAndExtremesInterval(false);
|
||||
|
||||
competitorDataSeries.setPoints(newRaceDataPoints, false);
|
||||
|
||||
changeMinMaxAndExtremesInterval(timeZoom.getA(), timeZoom.getB(), false);
|
||||
} else {
|
||||
competitorDataSeries.setPoints(newRaceDataPoints, false);
|
||||
}
|
||||
|
||||
setSeriesPoints(competitorDataSeries, newRaceDataPoints);
|
||||
// Adding the series if chart doesn't contain it
|
||||
if (!chartSeries.contains(competitorDataSeries)) {
|
||||
chart.addSeries(competitorDataSeries);
|
||||
@@ -335,14 +321,12 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > chartData.getRequestedFromTime().getTime()) {
|
||||
if (timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > chartData.getRequestedFromTime().getTime()) {
|
||||
timeOfEarliestRequestInMillis = chartData.getRequestedFromTime().getTime();
|
||||
}
|
||||
if(timeOfLatestRequestInMillis == null || timeOfLatestRequestInMillis < chartData.getRequestedToTime().getTime()) {
|
||||
if (timeOfLatestRequestInMillis == null || timeOfLatestRequestInMillis < chartData.getRequestedToTime().getTime()) {
|
||||
timeOfLatestRequestInMillis = chartData.getRequestedToTime().getTime();
|
||||
}
|
||||
|
||||
chart.redraw();
|
||||
}
|
||||
|
||||
@@ -482,6 +466,10 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
return this.selectedDetailType;
|
||||
}
|
||||
|
||||
private boolean hasReversedYAxis(DetailType detailType) {
|
||||
return detailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || detailType == DetailType.GAP_TO_LEADER_IN_SECONDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the {@link #selectedDetailType} field, clears the chart for the new <code>selectedDetailType</code> and
|
||||
* clears the {@link #chartData}.<br />
|
||||
@@ -492,12 +480,20 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
protected boolean setSelectedDetailType(DetailType newSelectedDetailType) {
|
||||
boolean hasDetailTypeChanged = newSelectedDetailType != this.selectedDetailType;
|
||||
if (hasDetailTypeChanged) {
|
||||
boolean oldReversedYAxis = hasReversedYAxis(this.selectedDetailType);
|
||||
this.selectedDetailType = newSelectedDetailType;
|
||||
clearChart();
|
||||
|
||||
chart.setTitle(new ChartTitle().setText(DetailTypeFormatter.format(selectedDetailType, stringMessages)),
|
||||
null);
|
||||
|
||||
// TODO There is a bug in the highcharts library which prevents to change the reverse property of the YAxis
|
||||
// Because we need this functionality we need to recreate the chart each time the YAxis changes
|
||||
if (oldReversedYAxis != hasReversedYAxis(selectedDetailType)) {
|
||||
chart = createChart();
|
||||
if (isZoomed) {
|
||||
Pair<Date, Date> zoomRange = timeRangeWithZoomProvider.getTimeZoom();
|
||||
onTimeZoomChanged(zoomRange.getA(), zoomRange.getB());
|
||||
} else {
|
||||
resetMinMaxAndExtremesInterval(/* redraw */ true);
|
||||
}
|
||||
}
|
||||
chart.setTitle(new ChartTitle().setText(DetailTypeFormatter.format(selectedDetailType, stringMessages)), null);
|
||||
final String unit = getDetailTypeUnit();
|
||||
if (!compactChart) {
|
||||
chart.getYAxis().setAxisTitleText(
|
||||
@@ -505,11 +501,8 @@ public abstract class AbstractChartPanel<SettingsType extends ChartSettings> ext
|
||||
} else {
|
||||
chart.getYAxis().setAxisTitleText("[" + unit + "]");
|
||||
}
|
||||
chart.getYAxis()
|
||||
.setReversed(
|
||||
(selectedDetailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || selectedDetailType == DetailType.GAP_TO_LEADER_IN_SECONDS) ? true
|
||||
: false);
|
||||
|
||||
chart.getYAxis().setReversed(
|
||||
selectedDetailType == DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER || selectedDetailType == DetailType.GAP_TO_LEADER_IN_SECONDS);
|
||||
final NumberFormat numberFormat = DetailTypeFormatter.getNumberFormat(selectedDetailType);
|
||||
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
|
||||
@Override
|
||||
|
||||
+21
-4
@@ -3,6 +3,8 @@ package com.sap.sailing.gwt.ui.shared.charts;
|
||||
import java.util.Date;
|
||||
|
||||
import org.moxieapps.gwt.highcharts.client.Chart;
|
||||
import org.moxieapps.gwt.highcharts.client.Point;
|
||||
import org.moxieapps.gwt.highcharts.client.Series;
|
||||
import org.moxieapps.gwt.highcharts.client.XAxis;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartClickEvent;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
|
||||
@@ -10,6 +12,7 @@ import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||
import com.sap.sailing.gwt.ui.actions.AsyncActionsExecutor;
|
||||
import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
|
||||
@@ -108,7 +111,6 @@ public abstract class RaceChart extends SimplePanel implements RaceSelectionChan
|
||||
|
||||
protected void changeMinMaxAndExtremesInterval(Date minTimepoint, Date maxTimepoint, boolean redraw) {
|
||||
XAxis xAxis = chart.getXAxis();
|
||||
|
||||
if (minTimepoint != null) {
|
||||
xAxis.setMin(minTimepoint.getTime());
|
||||
}
|
||||
@@ -120,26 +122,41 @@ public abstract class RaceChart extends SimplePanel implements RaceSelectionChan
|
||||
long tickInterval = (maxTimepoint.getTime() - minTimepoint.getTime()) / TICKCOUNT;
|
||||
xAxis.setTickInterval(tickInterval);
|
||||
}
|
||||
if(redraw) {
|
||||
if (redraw) {
|
||||
chart.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setSeriesPoints(Series series, Point[] points) {
|
||||
if (timeRangeWithZoomProvider.isZoomed()) {
|
||||
Pair<Date, Date> timeZoom = timeRangeWithZoomProvider.getTimeZoom();
|
||||
resetMinMaxAndExtremesInterval(/* redraw */ false);
|
||||
series.setPoints(points, false);
|
||||
changeMinMaxAndExtremesInterval(timeZoom.getA(), timeZoom.getB(), /* redraw */ false);
|
||||
} else {
|
||||
series.setPoints(points, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void resetMinMaxAndExtremesInterval(boolean redraw) {
|
||||
changeMinMaxAndExtremesInterval(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), redraw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeZoomChanged(Date zoomStartTimepoint, Date zoomEndTimepoint) {
|
||||
changeMinMaxAndExtremesInterval(zoomStartTimepoint, zoomEndTimepoint, true);
|
||||
// Probably there is a function for this in a newer version of highcharts: http://jsfiddle.net/mqz3N/1071/
|
||||
// chart.showResetZoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeRangeChanged(Date fromTime, Date toTime) {
|
||||
if(isZoomed && timer.getPlayMode() != PlayModes.Live)
|
||||
changeMinMaxAndExtremesInterval(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), true);
|
||||
if (!(isZoomed && timer.getPlayMode() == PlayModes.Live)) {
|
||||
resetMinMaxAndExtremesInterval(/* redraw */ true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeZoomReset() {
|
||||
resetMinMaxAndExtremesInterval(true);
|
||||
}
|
||||
|
||||
+3
-4
@@ -104,7 +104,7 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
|
||||
.setWidth100()
|
||||
.setHeight100()
|
||||
.setBorderColor(new Color("#CACACA"))
|
||||
.setBorderWidth(1)
|
||||
.setBorderWidth(0)
|
||||
.setBorderRadius(0)
|
||||
.setBackgroundColor(new Color("#EBEBEB"))
|
||||
.setPlotBorderWidth(0)
|
||||
@@ -381,11 +381,10 @@ public class WindChart extends RaceChart implements Component<WindChartSettings>
|
||||
newDirectionPoints = directionPoints;
|
||||
newSpeedPoints = speedPoints;
|
||||
}
|
||||
|
||||
directionSeries.setPoints(newDirectionPoints);
|
||||
setSeriesPoints(directionSeries, newDirectionPoints);
|
||||
windSourceDirectionPoints.put(windSource, newDirectionPoints);
|
||||
if (windSource.getType().useSpeed()) {
|
||||
speedSeries.setPoints(newSpeedPoints);
|
||||
setSeriesPoints(speedSeries, newSpeedPoints);
|
||||
windSourceSpeedPoints.put(windSource, newSpeedPoints);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ public interface IsEmbeddableComponent {
|
||||
|
||||
Widget getToolbarWidget();
|
||||
|
||||
Widget getLegendWidget();
|
||||
|
||||
BusyIndicator getBusyIndicator();
|
||||
|
||||
boolean isEmbedded();
|
||||
|
||||
+2
-19
@@ -17,8 +17,6 @@ import java.util.Set;
|
||||
import com.google.gwt.dom.client.Style;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.i18n.client.NumberFormat;
|
||||
import com.google.gwt.maps.client.InfoWindowContent;
|
||||
import com.google.gwt.maps.client.MapWidget;
|
||||
@@ -75,17 +73,16 @@ import com.sap.sailing.gwt.ui.client.TimeListener;
|
||||
import com.sap.sailing.gwt.ui.client.Timer;
|
||||
import com.sap.sailing.gwt.ui.client.Timer.PlayStates;
|
||||
import com.sap.sailing.gwt.ui.client.WindSourceTypeFormatter;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.CourseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LegInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.PositionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WindDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
|
||||
@@ -1286,21 +1283,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
LatLng latLng = LatLng.newInstance(maneuver.position.latDeg, maneuver.position.lngDeg);
|
||||
MarkerOptions options = MarkerOptions.newInstance();
|
||||
//TODO Introduce user role dependent view (Spectator, Admin)
|
||||
SpeedWithBearingDTO before = maneuver.speedWithBearingBefore;
|
||||
SpeedWithBearingDTO after = maneuver.speedWithBearingAfter;
|
||||
|
||||
String timeAndManeuver = DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.timepoint)
|
||||
+ ": " + maneuver.type.name();
|
||||
String directionChange = stringMessages.directionChange() + ": "
|
||||
+ ((int) maneuver.directionChangeInDegrees) + " "+stringMessages.degreesShort()+" ("
|
||||
+ ((int) before.bearingInDegrees) + " deg -> " + ((int) after.bearingInDegrees) + " "+stringMessages.degreesShort()+")";
|
||||
String speedChange = stringMessages.speedChange() + ": "
|
||||
+ NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" ("
|
||||
+ NumberFormat.getDecimalFormat().format(before.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+" -> "
|
||||
+ NumberFormat.getDecimalFormat().format(after.speedInKnots) + " "+stringMessages.averageSpeedInKnotsUnit()+")";
|
||||
String maneuverLoss = maneuver.maneuverLossInMeters == null ? "" : ("; "+stringMessages.maneuverLoss()+": "+
|
||||
NumberFormat.getDecimalFormat().format(maneuver.maneuverLossInMeters)+"m");
|
||||
options.setTitle(timeAndManeuver + "; " + directionChange + "; " + speedChange + maneuverLoss);
|
||||
options.setTitle(maneuver.toString(stringMessages));
|
||||
options.setIcon(raceMapImageManager.maneuverIconsForTypeAndTargetTack
|
||||
.get(new Util.Pair<ManeuverType, Tack>(maneuver.type, maneuver.newTack)));
|
||||
Marker marker = new Marker(latLng, options);
|
||||
|
||||
+1
-1
@@ -611,7 +611,7 @@ public class EventOverviewPanel extends FormPanel {
|
||||
result = textContainsStringsToCheck(event.name, name.split("\\s"));
|
||||
}
|
||||
if (result && onlyLiveCheckBox.getValue()) {
|
||||
result = event.leaderboardGroup.containsLiveRace();
|
||||
result = event.leaderboardGroup.hasLiveRace();
|
||||
}else if (result) {
|
||||
Date startDate = event.leaderboardGroup.getGroupStartDate();
|
||||
if (startDate != null) {
|
||||
|
||||
+1
-1
@@ -611,7 +611,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel {
|
||||
result = textContainsStringsToCheck(forGroup.name, name.split("\\s"));
|
||||
}
|
||||
if (result && onlyLiveCheckBox.getValue()) {
|
||||
result = forGroup.containsLiveRace();
|
||||
result = forGroup.hasLiveRace();
|
||||
}else if (result) {
|
||||
Date startDate = forGroup.getGroupStartDate();
|
||||
if (startDate != null) {
|
||||
|
||||
+2
-2
@@ -176,7 +176,7 @@ public class LeaderboardGroupPanel extends FormPanel implements HasWelcomeWidget
|
||||
mainPanel.add(leaderboardGroupDescriptionLabel);
|
||||
|
||||
DockPanel labelAndLegendPanel = new DockPanel();
|
||||
labelAndLegendPanel.setWidth("100%");
|
||||
labelAndLegendPanel.setWidth("90%");
|
||||
labelAndLegendPanel.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
|
||||
Label leaderboardsTableLabel = new Label(stringMessages.leaderboards());
|
||||
leaderboardsTableLabel.setStyleName(STYLE_NAME_PREFIX + "LeaderboardsTableLabel");
|
||||
@@ -236,7 +236,7 @@ public class LeaderboardGroupPanel extends FormPanel implements HasWelcomeWidget
|
||||
|
||||
LeaderboardGroupFullTableResources tableResources = GWT.create(LeaderboardGroupFullTableResources.class);
|
||||
CellTable<StrippedLeaderboardDTO> leaderboardsTable = new CellTable<StrippedLeaderboardDTO>(10000, tableResources);
|
||||
leaderboardsTable.setWidth("100%");
|
||||
leaderboardsTable.setWidth("90%");
|
||||
leaderboardsTable.setSelectionModel(new NoSelectionModel<StrippedLeaderboardDTO>());
|
||||
leaderboardsTable.addColumn(leaderboardNameColumn, stringMessages.regatta());
|
||||
leaderboardsTable.addColumn(overviewColumn, "");
|
||||
|
||||
+1
-3
@@ -56,10 +56,8 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements RegattaRe
|
||||
&& Window.Location.getParameter("embedded").equalsIgnoreCase("true");
|
||||
if (!embedded) {
|
||||
String title = groupName != null ? groupName : stringMessages.overview();
|
||||
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(title, stringMessages);
|
||||
LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(title, stringMessages, this);
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
|
||||
FlowPanel globalNavigationPanel = new GlobalNavigationPanel(stringMessages, true, null, null);
|
||||
logoAndTitlePanel.add(globalNavigationPanel);
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class TVEntryPoint extends AbstractEntryPoint {
|
||||
if(leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
|
||||
leaderboardDisplayName = leaderboardName;
|
||||
}
|
||||
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages);
|
||||
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, stringMessages, this);
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
mainPanel.addNorth(logoAndTitlePanel, 68);
|
||||
}
|
||||
|
||||
+6
-19
@@ -72,7 +72,8 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
|
||||
showWindChart = false;
|
||||
leaderboard = null;
|
||||
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false);
|
||||
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false,
|
||||
/* showMetaLeaderboardsOnSamePage */ false);
|
||||
|
||||
timer = new Timer(PlayModes.Live, /* delayBetweenAutoAdvancesInMilliseconds */1000l);
|
||||
timer.setLivePlayDelayInMillis(delayToLiveInMillis);
|
||||
@@ -85,8 +86,8 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
private LeaderboardPanel createLeaderboardPanel(String leaderboardName, boolean showRaceDetails) {
|
||||
CompetitorSelectionModel selectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true);
|
||||
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardSettings,
|
||||
/* preSelectedRace */null, selectionModel, timer, leaderboardName, null, errorReporter, stringMessages,
|
||||
userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false) {
|
||||
/* preSelectedRace */null, selectionModel, timer, leaderboardName, errorReporter, stringMessages, userAgent,
|
||||
showRaceDetails, /* raceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false) {
|
||||
@Override
|
||||
protected void setLeaderboard(LeaderboardDTO leaderboard) {
|
||||
super.setLeaderboard(leaderboard);
|
||||
@@ -94,6 +95,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
updateRaceTimesInfoProvider();
|
||||
}
|
||||
};
|
||||
leaderboardPanel.addStyleName(LeaderboardPanel.LEADERBOARD_MARGIN_STYLE);
|
||||
return leaderboardPanel;
|
||||
}
|
||||
|
||||
@@ -135,30 +137,15 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
}
|
||||
|
||||
private void showLeaderboard() {
|
||||
if(activeTvView != TVViews.Leaderboard) {
|
||||
if (activeTvView != TVViews.Leaderboard) {
|
||||
clearContentPanels();
|
||||
|
||||
LeaderboardPanel leaderboardPanel = createLeaderboardPanel(leaderboardName, showRaceDetails);
|
||||
ScrollPanel leaderboardContentPanel = new ScrollPanel();
|
||||
leaderboardContentPanel.add(leaderboardPanel);
|
||||
|
||||
// if(leaderboard != null) {
|
||||
// //Resetting the settings of the leaderboard panel to prevent, that some race columns get lost
|
||||
// List<String> namesOfRaceColumnsToShow = new ArrayList<String>();
|
||||
// for (RaceColumnDTO race : leaderboard.getRaceList()) {
|
||||
// namesOfRaceColumnsToShow.add(race.getRaceColumnName());
|
||||
// }
|
||||
// LeaderboardSettings settings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||
// namesOfRaceColumnsToShow, null, null, false);
|
||||
// leaderboardPanel.updateSettings(settings);
|
||||
// }
|
||||
|
||||
dockPanel.add(leaderboardContentPanel);
|
||||
|
||||
if(logoAndTitlePanel != null) {
|
||||
logoAndTitlePanel.setSubTitle(leaderboardName);
|
||||
}
|
||||
|
||||
currentLiveRace = null;
|
||||
activeTvView = TVViews.Leaderboard;
|
||||
}
|
||||
|
||||
@@ -61,12 +61,12 @@
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>RaceRankingAsRssFeedServlet</servlet-name>
|
||||
<servlet-class>com.sap.sailing.server.gateway.impl.RaceRankingAsRssFeedServlet</servlet-class>
|
||||
<servlet-name>LiveRaceRankingAsRssFeedServlet</servlet-name>
|
||||
<servlet-class>com.sap.sailing.server.gateway.impl.LiveRaceRankingAsRssFeedServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>RaceRankingAsRssFeedServlet</servlet-name>
|
||||
<url-pattern>/rss/raceranking</url-pattern>
|
||||
<servlet-name>LiveRaceRankingAsRssFeedServlet</servlet-name>
|
||||
<url-pattern>/rss/liveRaceRanking</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet id="jsp">
|
||||
|
||||
+68
-49
@@ -26,64 +26,88 @@ import com.sun.syndication.feed.synd.SyndFeed;
|
||||
import com.sun.syndication.feed.synd.SyndFeedImpl;
|
||||
import com.sun.syndication.io.SyndFeedOutput;
|
||||
|
||||
public class RaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
|
||||
public class LiveRaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
|
||||
private static final long serialVersionUID = -508373964426868319L;
|
||||
private static final String PARAM_NAME_LEADERBOARDNAME = "leaderboardName";
|
||||
private static final String PARAM_NAME_RACENAME = "raceName";
|
||||
private static final String PARAM_MAX_COMPETITORS = "max";
|
||||
private String charEncoding = "UTF-8";
|
||||
|
||||
private int maxCompetitorsToShow = 1000;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
// to allow access to the json document directly from a client side javascript
|
||||
// to allow access to the rss feed directly from a client side javascript
|
||||
resp.setHeader("Access-Control-Allow-Origin", "*");
|
||||
resp.setContentType("application/rss+xml");
|
||||
resp.setCharacterEncoding(charEncoding);
|
||||
|
||||
TimePoint timePoint = MillisecondsTimePoint.now();
|
||||
String leaderboardName = req.getParameter(PARAM_NAME_LEADERBOARDNAME);
|
||||
String raceName = req.getParameter(PARAM_NAME_RACENAME);
|
||||
String maxCompetitorsParam = req.getParameter(PARAM_MAX_COMPETITORS);
|
||||
if(maxCompetitorsParam != null) {
|
||||
try {
|
||||
maxCompetitorsToShow = Integer.parseInt(maxCompetitorsParam);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (leaderboardName == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Need to specify a leaderboard name using the "+
|
||||
PARAM_NAME_LEADERBOARDNAME+" parameter");
|
||||
} else if (raceName == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Need to specify a race column name using the "+
|
||||
PARAM_NAME_RACENAME+" parameter");
|
||||
} else {
|
||||
Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard == null) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Leaderboard "+leaderboardName+" not found");
|
||||
} else {
|
||||
try {
|
||||
RaceColumn raceColumnToShow = null;
|
||||
|
||||
RaceColumn raceColumnToShow = null;
|
||||
TrackedRace liveTrackedRace = null;
|
||||
|
||||
// find a live race in the leaderboard
|
||||
Iterable<RaceColumn> races = leaderboard.getRaceColumns();
|
||||
for (RaceColumn raceInLeaderboard : races) {
|
||||
for (Fleet fleet : raceInLeaderboard.getFleets()) {
|
||||
TrackedRace trackedRace = raceInLeaderboard.getTrackedRace(fleet);
|
||||
if (trackedRace != null && raceInLeaderboard.getName().equals(raceName)) {
|
||||
raceColumnToShow = raceInLeaderboard;
|
||||
break;
|
||||
TrackedRace trackedRaceOfFleet = raceInLeaderboard.getTrackedRace(fleet);
|
||||
if(trackedRaceOfFleet != null && isLive(trackedRaceOfFleet)) {
|
||||
raceColumnToShow = raceInLeaderboard;
|
||||
liveTrackedRace = trackedRaceOfFleet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(raceColumnToShow != null) {
|
||||
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumnToShow, MillisecondsTimePoint.now());
|
||||
String eventName = "STG meets 'Berlin Match Race'";
|
||||
String title = eventName + ": " + leaderboardName;
|
||||
SyndFeed feed = new SyndFeedImpl();
|
||||
String title = leaderboard.getName();
|
||||
feed.setTitle(title);
|
||||
feed.setFeedType("rss_2.0");
|
||||
feed.setLink("http://www.sapsaling.com");
|
||||
feed.setDescription("SAP Sailing Analytics");
|
||||
feed.setPublishedDate(new Date());
|
||||
feed.setEncoding(charEncoding);
|
||||
|
||||
List<SyndEntry> entries = new ArrayList<SyndEntry>();
|
||||
SyndContent description = new SyndContentImpl();
|
||||
SyndEntry entry = new SyndEntryImpl();
|
||||
entry.setLink("http://www.sapsailing.com");
|
||||
description.setType("text/plain");
|
||||
|
||||
if(raceColumnToShow != null && liveTrackedRace != null) {
|
||||
String entryTitle = "Live ranking of race '" + raceColumnToShow.getName() + "'";
|
||||
entry.setTitle(entryTitle);
|
||||
|
||||
|
||||
SyndFeed feed = new SyndFeedImpl();
|
||||
feed.setTitle(title);
|
||||
feed.setFeedType("rss_2.0");
|
||||
feed.setLink("http://www.sapsaling.com");
|
||||
feed.setDescription("SAP Sailing Analytics");
|
||||
feed.setPublishedDate(new Date());
|
||||
feed.setEncoding(charEncoding);
|
||||
|
||||
List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumnToShow, timePoint);
|
||||
int currentCompetitorCounter = 0;
|
||||
String feedText = "";
|
||||
|
||||
for (Competitor competitor : competitorsFromBestToWorst) {
|
||||
if(currentCompetitorCounter == maxCompetitorsToShow) {
|
||||
break;
|
||||
}
|
||||
currentCompetitorCounter++;
|
||||
String sailID = competitor.getBoat().getSailID();
|
||||
feedText += currentCompetitorCounter + ". " + sailID + "\n";
|
||||
|
||||
// String competitorName = competitor.getName();
|
||||
// final String displayName = leaderboard.getDisplayName(competitor);
|
||||
// String id = competitor.getId().toString();
|
||||
@@ -92,7 +116,6 @@ public class RaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
|
||||
// String countryCode = nationality != null ? nationality.getCountryCode().getTwoLetterISOCode(): null;
|
||||
// String totalRank = "" +competitorsFromBestToWorst.indexOf(competitor) + 1;
|
||||
// String totalPoints = "" + leaderboard.getTotalPoints(competitor, timePoint);
|
||||
//
|
||||
// String raceColumnName = raceColumnToShow.getName();
|
||||
// final Fleet fleetOfCompetitor = raceColumnToShow.getFleetOfCompetitor(competitor);
|
||||
// String fleet = fleetOfCompetitor==null?"":fleetOfCompetitor.getName();
|
||||
@@ -103,37 +126,33 @@ public class RaceRankingAsRssFeedServlet extends SailingServerHttpServlet {
|
||||
// String isDiscarded = String.valueOf(leaderboard.isDiscarded(competitor, raceColumnToShow, timePoint));
|
||||
// String isCorrected = String.valueOf(leaderboard.getScoreCorrection().isScoreCorrected(competitor, raceColumnToShow));
|
||||
|
||||
final TrackedRace trackedRace = raceColumnToShow.getTrackedRace(competitor);
|
||||
|
||||
boolean isLive = trackedRace != null && trackedRace.getEndOfRace() == null && (trackedRace.getStartOfTracking() != null ? new Date().after(trackedRace.getStartOfTracking().asDate()) : false);
|
||||
if(isLive) {
|
||||
String raceRank = "" + trackedRace.getRank(competitor, timePoint);
|
||||
feedText += raceRank + ". " + sailID + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
List<SyndEntry> entries = new ArrayList<SyndEntry>();
|
||||
SyndContent description = new SyndContentImpl();
|
||||
|
||||
SyndEntry entry = new SyndEntryImpl();
|
||||
entry.setTitle("Live Race ranking of race " + raceName);
|
||||
entry.setLink("http://obmr2012.sapsailing.com");
|
||||
description.setType("text/plain");
|
||||
description.setValue(feedText);
|
||||
entry.setDescription(description);
|
||||
entries.add(entry);
|
||||
feed.setEntries(entries);
|
||||
|
||||
PrintWriter writer = resp.getWriter();
|
||||
SyndFeedOutput feedOutput = new SyndFeedOutput();
|
||||
feedOutput.output(feed, writer);
|
||||
} else {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "No live race at the moment");
|
||||
String entryTitle = "No live race right now";
|
||||
entry.setTitle(entryTitle);
|
||||
description.setValue("");
|
||||
}
|
||||
|
||||
entry.setDescription(description);
|
||||
entries.add(entry);
|
||||
feed.setEntries(entries);
|
||||
|
||||
PrintWriter writer = resp.getWriter();
|
||||
SyndFeedOutput feedOutput = new SyndFeedOutput();
|
||||
feedOutput.output(feed, writer);
|
||||
} catch (Exception e) {
|
||||
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>true</code> if the startOfTracking is after the current date and there's no end of the race
|
||||
*/
|
||||
private boolean isLive(TrackedRace trackedRace) {
|
||||
return trackedRace.getEndOfRace() == null && (trackedRace.getStartOfTracking() != null ? new Date().after(trackedRace.getStartOfTracking().asDate()) : false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,8 +4,6 @@ bin.includes = META-INF/,\
|
||||
WEB-INF/classes/,\
|
||||
WEB-INF/web.xml,\
|
||||
images/,\
|
||||
winnercircle2012/,\
|
||||
main.css,\
|
||||
ess40/,\
|
||||
fontface.css,\
|
||||
sap.ico,\
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</p>
|
||||
</div>
|
||||
<div id='navigation'>
|
||||
<a class='navButton event' href='http://ess40-2012.sapsailing.com/gwt/Leaderboard.html?name=Extreme%2040%20Rio&refreshIntervalMillis=10000&lastN=3&raceDetail=DISPLAY_LEGS'>
|
||||
<a class='navButton event' href='http://ess40-2012.sapsailing.com/gwt/Leaderboard.html?name=Extreme%2040%20Rio&refreshIntervalMillis=10000&lastN=3'>
|
||||
Live Rio Act Leaderboard
|
||||
</a>
|
||||
<a class='navButton overall' href='http://ess40-2012.sapsailing.com/gwt/Leaderboard.html?name=Extreme%20Sailing%20Series%202012%20Overall&showRaceDetails=false&refreshIntervalMillis=10000&displayName=Overall%20Standings&leaderboardGroupName=Extreme%20Sailing%20Series%202012&root=leaderboardGroupPanel'>
|
||||
|
||||
Reference in new issue
Block a user