diff --git a/configuration/unique_ips_per_referrer b/configuration/unique_ips_per_referrer new file mode 100755 index 00000000000..22e455f4517 --- /dev/null +++ b/configuration/unique_ips_per_referrer @@ -0,0 +1,20 @@ +#!/bin/bash + +# groups Apache / httpd log entries into "${referrer}.ips" files, where ${referrer} is the +# referrer URL identifying the "event"; the lines written to the .ips files hold the IP +# address of the requestor, the date (not the time) and the user agent string. +# Sorting for unique entries should give a count similar to what goaccess is using to +# determine "unique visitors." + +# A sample line: +# 505Worlds2012.sapsailing.com 66.249.73.53 - - [12/Jan/2014:03:33:11 +0000] "GET /robots.txt HTTP/1.1" 404 238 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" + +#cat $* | sed -e 's/^\([^ ]*\) \([^ ]*\) \([^ ]*\) \([^ ]*\) \[\([^:]*\):\([^]]*\)\] \"[^\"]*\" [^ ]* [^ ]* \"[^\"]\" \"\([\"]*\)\"\(.*\)$/\1 \2 \5 \7/' +cat $* | sed -e 's/^\([^ ]*\) \([^ ]*\) \([^ ]*\) \([^ ]*\) \[\([^:]*\):\([^]]*\)\] \"[^\"]*\" [^ ]* [^ ]* \"[^\"]*\" \"\([^\"]*\)\"/\1 \2 \5 \7/' | while read referrer hit; do + echo "$hit" >>${referrer}.ips +done +for i in *.ips; do + echo -n "$i " + cat $i | sort -u | wc | awk '{ print $1; }' +done | tee unique-ips-days-useragents-per-event +echo "Done. Wrote results to `pwd`/unique-ips-days-useragents-per-event diff --git a/java/CodeFormatter_IntelliJ.jar b/java/CodeFormatter_IntelliJ.jar index 5b4f7c50d75..1df4afba956 100644 Binary files a/java/CodeFormatter_IntelliJ.jar and b/java/CodeFormatter_IntelliJ.jar differ diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/RibDashboardEntryPoint.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/RibDashboardEntryPoint.java index 564c84d4e51..ea17424f04e 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/RibDashboardEntryPoint.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/RibDashboardEntryPoint.java @@ -29,6 +29,7 @@ public class RibDashboardEntryPoint extends AbstractEntryPoint { @Override public void doOnModuleLoad() { + SailingServiceAsync sailingService = GWT.create(SailingService.class); EntryPointHelper.registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.WEB_CONTEXT_PATH, RemoteServiceMappingConstants.sailingServiceRemotePath); diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/startanalysis/StartlineAnalysisCard.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/startanalysis/StartlineAnalysisCard.java index 034fd34c083..df99f1c9f2c 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/startanalysis/StartlineAnalysisCard.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/startanalysis/StartlineAnalysisCard.java @@ -148,7 +148,7 @@ public class StartlineAnalysisCard extends Composite implements HasWidgets, Star asyncActionsExecutor, null, Collections.singletonList(startAnalysisDTO.regattaAndRaceIdentifier), 5000l /* requestInterval */); raceMap = new RaceMap(sailingServiceAsync, asyncActionsExecutor, null, timer, competitorSelectionModel, StringMessages.INSTANCE, false, false, false, startAnalysisDTO.regattaAndRaceIdentifier, - resources.combinedWindPanelStyle()); + resources.combinedWindPanelStyle(), /* showHeaderPanel */ true); raceMap.onRaceSelectionChange(Collections.singletonList(startAnalysisDTO.regattaAndRaceIdentifier)); raceMap.getSettings().setZoomSettings(new RaceMapZoomSettings(zoomTypes, false)); raceMap.getSettings().setHelpLinesSettings(getHelpLineSettings()); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindSourceType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindSourceType.java index 9f634453523..82bc51cfa6e 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindSourceType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindSourceType.java @@ -27,9 +27,10 @@ public enum WindSourceType { TRACK_BASED_ESTIMATION(false, 0.5, /* useSpeed */ false), /** - * Inferred from the race course layout if the course is known to have its first leg be an upwind leg + * Inferred from the race course layout if the course is known to have its first leg be an upwind leg. This + * source has very low confidence and must be superseded by any other wind source. */ - COURSE_BASED(false, 0.3, /* useSpeed */ false), + COURSE_BASED(false, 0.01, /* useSpeed */ false), /** * Wind estimation combined from all other wind sources, using TrackedRace.getWind(...), based on diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java index b4ac93865e0..7b297db56cf 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/RaceDefinitionImpl.java @@ -1,7 +1,10 @@ package com.sap.sailing.domain.base.impl; import java.io.Serializable; +import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.Set; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; @@ -13,6 +16,7 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { private static final long serialVersionUID = -1900955198751393727L; private final Course course; private final LinkedHashMap competitorsById; + private final Set competitors; private final BoatClass boatClass; private final Serializable id; @@ -23,11 +27,12 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { public RaceDefinitionImpl(String name, Course course, BoatClass boatClass, Iterable competitors, Serializable id) { super(name); assert name != null; - this.course = course; + this.competitors = new HashSet<>(); this.competitorsById = new LinkedHashMap<>(); for (Competitor competitor : competitors) { Competitor competitorWithEqualID = competitorsById.put(competitor.getId(), competitor); + this.competitors.add(competitor); if (competitorWithEqualID != null && competitorWithEqualID != competitor) { throw new IllegalArgumentException("Two distinct competitors with equal ID "+competitor.getId()+" are not allowed within the single race "+name); } @@ -48,7 +53,7 @@ public class RaceDefinitionImpl extends NamedImpl implements RaceDefinition { @Override public Iterable getCompetitors() { - return competitorsById.values(); + return Collections.unmodifiableSet(competitors); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java index 1fd99932582..3182d990e1a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/ScoreCorrection.java @@ -99,6 +99,14 @@ public interface ScoreCorrection extends Serializable { */ boolean hasCorrectionFor(RaceColumn raceInLeaderboard); + /** + * Similar to {@link #hasCorrectionFor(RaceColumn)}, but returns true only if score corrections are found + * for any competitor who in raceInLeaderboard is not in a tracked race and hence the fleet assignment + * cannot be determined. This is helpful, e.g., for progress detection. If score corrections are present for such + * untracked competitors then all untracked fleets need to be assumed as finished. + */ + boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard); + /** * @return all race columns for which this score corrections object has at least one correction; note that this * object may hold corrections also for competitors that are not currently returned by diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java index b58f433eb1d..a0891127129 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/ScoreCorrectionImpl.java @@ -472,19 +472,30 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection { @Override public boolean hasCorrectionFor(RaceColumn raceInLeaderboard) { + return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ false); + } + + private boolean internalHasScoreCorrectionFor(RaceColumn raceInLeaderboard, boolean considerOnlyUntrackedRaces) { for (com.sap.sse.common.Util.Pair correctedScoresKey : correctedScores.keySet()) { - if (correctedScoresKey.getB() == raceInLeaderboard) { + if (correctedScoresKey.getB() == raceInLeaderboard && + (!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(correctedScoresKey.getA()) == null)) { return true; } } for (com.sap.sse.common.Util.Pair maxPointsReasonsKey : maxPointsReasons.keySet()) { - if (maxPointsReasonsKey.getB() == raceInLeaderboard) { + if (maxPointsReasonsKey.getB() == raceInLeaderboard && + (!considerOnlyUntrackedRaces || raceInLeaderboard.getTrackedRace(maxPointsReasonsKey.getA()) == null)) { return true; } } return false; } + @Override + public boolean hasCorrectionForNonTrackedFleet(RaceColumn raceInLeaderboard) { + return internalHasScoreCorrectionFor(raceInLeaderboard, /* considerOnlyUntrackedRaces */ true); + } + @Override public TimePoint getTimePointOfLastCorrectionsValidity() { return timePointOfLastCorrectionsValidity; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 076898280e9..3a5e35dbc8c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -139,7 +139,6 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.Timed; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; -import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.concurrent.LockUtil; import com.sap.sse.concurrent.NamedReentrantReadWriteLock; @@ -3669,7 +3668,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl if (polarDataService == null) { throw new NotEnoughDataHasBeenAddedException("Target time estimation failed. No polar service available."); } - Duration durationOfAllLegs = new MillisecondsDurationImpl(0); + Duration durationOfAllLegs = Duration.NULL; for (TrackedLeg leg : trackedLegs.values()) { Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint); durationOfAllLegs = durationOfAllLegs.plus(durationOfLeg); diff --git a/java/com.sap.sailing.gwt.ui/.tmp/gwt-work/.gitignore b/java/com.sap.sailing.gwt.ui/.tmp/gwt-work/.gitignore new file mode 100644 index 00000000000..3e3e47daf0f --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/.tmp/gwt-work/.gitignore @@ -0,0 +1 @@ +com.* diff --git a/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.css b/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.css new file mode 100644 index 00000000000..6376c253b35 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.css @@ -0,0 +1,695 @@ +/** Add css rules here for your application. */ +/* + body #calendar:after + content: " "; + display: block; + height: 0; + clear: both; + visibility: hidden; +*/ + + + * { + font-family: 'Open Sans', sans-serif; + font-size: 12px; +} + + +iframe { + display: block; + width: 100%; + height: 100%; + border: none; + overflow-y: auto; + overflow-x: hidden; +} + +.clear { + clear: both; + font-size: 1px; + height: 0px; + line-height: 0px; + margin: -1px 0 0; + width: 100%; +} + +.cover { + left: -1000px; + overflow: hidden; + position: absolute; + top: -1000px; +} + +body { + background: white; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + line-height: 1; + font-size: 14px; + font-weight: 400; + margin: 0; + +} + +table td, select, button { + font-family: 'Open Sans', Arial, Verdana, sans-serif; + line-height: 1; + font-size: 14px; +} + +.contentOuterPanel { + background: #dddcdc; + margin: 0 12px 0 12px; + padding: 12px; +} + + + +/** Example rules used by the template application (remove for your app) */ +h1 { + font-size: 2em; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 700; + color: #777777; + margin: 40px 0px 70px; + text-align: center; +} + +table { + border-collapse: separate; +} + +.CombinedWindPanelParentDiv { + padding-top: 34px; + padding-left: 10px; +} + +.TrueNorthIndicatorPanelParentDiv { + padding-top: 10px; + padding-left: 10px; +} + +.MapSimulationLegendParentDiv { + padding-bottom: 10px; + padding-right: 10px; + pointer-events: none; +} + +.MapSimulationLegend { + pointer-events: all; +} + +.RaceMap-HeaderPanel { + height: 60px; + width: 100%; + background-color: #333; + opacity: .5; +} + +.leaderboardLabel { + border-bottom: 2px solid #007dc0; + padding: 0 0 6px 0; + font-weight: 700; +} + +.refreshAndSettings { + background: #f2f2f2; + border-bottom: 2px solid #fff; + border-top: 2px solid #fff; + height: 30px; +} + +.refreshAndSettings table td { + padding: 0 10px 0 0; +} + +.refreshAndSettings table table td { + padding: 0 0 0 0; +} + +.race-name { + line-height: 1.0; +} + +.openColumn { + position: relative; + vertical-align: middle; +} + +img.openColumn { + display: block; + margin: 3px auto 3px auto !important; +} + +.breadcrumbPanel { + height: 30px; + margin: 0 12px 13px 12px; +} + +.mainPanel { + width: 100%; +} + +.raceBoardNavigation { + float: left; + margin: 9px 0 0 12px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; +} + +.raceBoardControls { + float: right; + margin: 9px 0 0 12px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; +} + +.raceBoardNavigation-navigationitem { + color: #dddcdc; + text-transform: uppercase; + font-weight: 500; + margin: 0 12px 0 0; + padding: 0 0 5px 0; +} + +.raceBoardNavigation-navigationitem:hover { + border-bottom: 4px solid #f0ab00; + color: #dddcdc; + text-decoration: none; +} + +.raceBoardNavigation-navigationitem:active { + border-bottom: 4px solid #f0ab00; + color: #dddcdc; + text-decoration: none; +} + +.competitorInfo-Canvas { + pointer-events: none; +} + +.gwt-DialogBox { + z-index: 20; +} + +.abstractChartPanel-importantMessageOfChart { + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; + font-size: 20px; + height: 70px; + margin: 0 auto; + padding: 49px 0 0 80px; + width: 300px; + color: #7c7c7c; + background: url(images/important-message.png) left center no-repeat; +} + +.breadcrumbPanel-NextArrow { + display: none; +} + +.errorLabel { + color: #FF0000; +} + +.raceBoardNavigation-settingsButtonPanel { + float: right; + margin: 9px 12px 0 0; +} + +.raceBoardNavigation-settingsButton { + background: url(images/settings_white.png), + url(images/btn-cta-orangeleft.png); + background-position: center, center 0; + background-repeat: no-repeat, repeat-x; + width: 27px; + height: 23px; + margin: 0 12px 0 1px; + -webkit-border-radius: 0px 3px 3px 0px; + -moz-border-radius: 0px 3px 3px 0px; + -o-border-radius: 0px 3px 3px 0px; + -ms-border-radius: 0px 3px 3px 0px; + border-radius: 0px 3px 3px 0px; +} + +.raceBoardNavigation-settingsButton:hover { + background-image: url(images/settings_white.png), + url(images/btn-cta-orangeleft.png); + background-position: center, center -30px; + background-repeat: no-repeat, repeat-x; + cursor: pointer; +} + +.raceBoardNavigation-filterButton:hover { + background-image: url(images/filter_white.png), + url(images/btn-cta-orangeleft.png); + background-position: center, center -30px; + background-repeat: no-repeat, repeat-x; + cursor: pointer; +} + +.raceBoardNavigation-innerElement { + background: url("images/btn-cta-orangeleft.png") repeat-x scroll center + 0 transparent; + color: #FFFFFF; + cursor: pointer; + font-family: 'Open Sans', Verdana, Arial, sans-serif; + font-weight: 600; + font-size: 14px; + margin: 0 0 0 0; + padding: 2px 7px 2px 3px; + height: 18px; + display: inline-block; + text-decoration: none; + border-radius: 5px 0 0 5px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25); + border-bottom: 1px solid rgba(0, 0, 0, 0.25); +} + +.raceBoardNavigation-innerElement:active { + top: 1px; + position: relative; +} + +.raceBoardNavigation-innerElement:hover { + background: url("images/btn-cta-orangeleft.png") repeat-x scroll center + -30px transparent; + cursor: pointer; +} + +.raceBoardNavigation-innerElement label { + cursor: pointer; +} + +.raceBoardNavigation-standaloneElement { + background: url("images/btn-cta-orangeleft.png") repeat-x scroll center + 0 transparent; + color: #FFFFFF; + cursor: pointer; + font-family: 'Open Sans', Verdana, Arial, sans-serif; + font-weight: 600; + font-size: 14px; + margin: 0 0 0 0; + padding: 2px 7px 2px 3px; + height: 23px; + display: inline-block; + text-decoration: none; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25); + border-bottom: 1px solid rgba(0, 0, 0, 0.25); + border-radius: 5px 5px 5px 5px; + +} + +.raceBoardNavigation-standaloneElement:active { + top: 1px; + position: relative; +} + +.raceBoardNavigation-standaloneElement:hover { + background: url("images/btn-cta-orangeleft.png") repeat-x scroll center + -30px transparent; + cursor: pointer; +} + +.raceBoardNavigation-standaloneElement label { + cursor: pointer; +} + +.timePanelButton { + float: left; + margin: 0 0 0 5px; +} + +.timePanelButton-SlowDown { + background-color: red !important; +} + +.timePanelButton-SpeedUp { + background-color: green !important; +} + +.playPauseButton { + padding: 0; + float: left; + width: 32px; + height: 28px; + background: url(images/viewicons/@1x/img/SAP_RV_Play_INACTIVE.png) + center no-repeat; + cursor: pointer; + border: 1px solid #cecece; + border-radius: 4px; + outline: 0; +} + +.playPauseButton:hover { + cursor: pointer; + background-color: #cecece; +} + +.playPauseButtonPause { + background: url(images/viewicons/@2x/img/SAP_RV_Pause_INACTIVE.png) 2px + 0 no-repeat; + outline: 0; + background-size: contain; +} + +.playPauseButtonPause:hover { + cursor: pointer; +} + +.backToLivePlayButton { + width: 42px; + height: 38px; + font-size: 8px; + white-space: normal; + border-radius: 4px; + border: none; + + margin-right: 23px; + margin-top: 5px; + margin-bottom: 5px; + z-index: 1; +} + +.backToLivePlayButton-Inactive { + color: #6dc021; + background-color: white; + font-weight: bold; +} + +.backToLivePlayButton-Active { + background-color: #6dc021; + color: white; + font-weight: normal; +} + +.backToLivePlayButton:hover { + border: 2px solid gray; +} + +.backToLivePlayButton:focus { + outline: 0 !important; +} + +.dockLayoutPanel div[style="position: absolute; overflow: hidden; left: 0px; right: 0px; bottom: 0px; height: 90px;"] + { + z-index: 10; +} + +.browserOptimizedMessage { + padding: 15px 0px; +} + +.trackingQuality-circle { + width: 8px; + height: 8px; + border-radius: 50%; + float: left; +} + +.circleYellow { + background: #fbba00; +} + +.circleGreen { + background: #8ab54e; +} + +.circleRed { + background: #f01010; +} + +.busyIndicator-Simple { + position: absolute; + top: 0.7em; + right: 7.4em; +} + +.busyIndicator-Circle { + +} + +.raceBoard-TitlePanel { + position: relative; + top: 10px; + z-index: 10; + color: #fff; + margin-left: auto; + margin-right: auto; +} + +.raceBoard-TitlePanel-RaceNameLabel { + font-size: 32px; + font-weight: bold; + padding: 7px; +} + +.raceBoard-Logo { + position: absolute; + top: 6px; + left: 10px; + z-index: 10; + cursor: pointer; +} + +.SideBySideComponentViewer-MainPanel { + top: 12px; +} + +.TimePanel-ShowExtended-Button { + width: 29px; + height: 28px; + float: left; + cursor: pointer; + border-radius: 4px; + border: 1px solid #ececec; +} + +.TimePanel-ShowExtended-Button-Open { + background: white url(images/arrows_bottom_black.png) center no-repeat; +} + +.TimePanel-ShowExtended-Button-Closed { + background: white url(images/arrows_top_black.png) center no-repeat; +} + +.TimePanel-ShowExtended-Button:hover { + background-color: #cecece; +} + +.TimePanel-ShowExtended-Button:focus { + outline: 0 !important; +} + +.RaceBoard-globalNavigation-Link { + display: inline; + margin-right: 3px; +} + +.RaceBoard-globalNavigation-Link a { + color: #fff; + cursor: pointer; +} + +.RaceBoard-globalNavigation-Link+.RaceBoard-globalNavigation-Link:before + { + content: ' \00bb\00a0'; +} + +.TimePanel-settingsButtonStyle { + position: absolute; + right: 0px; + z-index: 1; + border: none; +} + +.TimePanel-settingsButtonStyle:focus { + outline: 0 !important; +} + +.TimePanel-settingsButtonStyle:hover { + border: 1px solid gray; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-multiCompetitorRaceChart + { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_INACTIVE.png), + url(images/arrows_top_black_small.png); + background-repeat: no-repeat, no-repeat; + background-position: 5% 50%, 95% 50% +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open-multiCompetitorRaceChart + { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_GHOSTED.png), + url(images/arrows_bottom_black_small.png); + background-repeat: no-repeat, no-repeat; + background-position: 5% 50%, 95% 50% +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-windChart { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_INACTIVE.png), + url(images/arrows_top_black_small.png); + background-repeat: no-repeat, no-repeat; + background-position: 5% 50%, 95% 50% +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open-windChart { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_CompetitorCharts_GHOSTED.png), + url(images/arrows_bottom_black_small.png); + background-repeat: no-repeat, no-repeat; + background-position: 5% 50%, 95% 50% +} + +.gwt-SplitLayoutPanel-EastToggleButton-Open-leaderboard { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_Leaderboard_GHOSTED.png), + url(images/arrows_top_black_small.png); + background-repeat: no-repeat, no-repeat; + background-position: 4% 50%, 96% 50% +} + +.gwt-SplitLayoutPanel-EastToggleButton-Closed-leaderboard { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_Leaderboard_INACTIVE.png), + url(images/arrows_bottom_black_small.png); + background-repeat: no-repeat, no-repeat; + background-position: 4% 50%, 96% 50% +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed-media, + .gwt-SplitLayoutPanel-NorthSouthToggleButton-Open-media { + background-color: white; + background-image: + url(images/viewicons/@1x/img/SAP_RV_AudioVideo_INACTIVE.png); + background-repeat: no-repeat, no-repeat; + background-position: 10% 50%, 90% 50% +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-mediaplaying { + background-image: + url(images/viewicons/@1x/img/SAP_RV_AudioVideo_GHOSTED.png); +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Small-media { + background-position: 5% 50%, 100% 50% !important; + margin-left: 0px !important; + padding: 2px 35px !important; +} + +@external gwt-ToggleButton-up, gwt-ToggleButton-down, gwt-ToggleButton-up-hovering, gwt-ToggleButton-down-hovering; +.gwt-ToggleButton-up, .gwt-ToggleButton-up-hovering { + background: url(images/unlock.png) center no-repeat; + margin-left: 5px; +} + +.gwt-ToggleButton-down, .gwt-ToggleButton-down-hovering { + background: url(images/lock.png) center no-repeat; + margin-left: 5px; +} + +.RegattaRaceInformation-Header { + font-family: 'Open Sans', Arial, Verdana, sans-serif; + position: relative; + left: 140px; + top: 11px; +} + +.RegattaRaceInformation-Header .RaceName-Label { + display: inline; + color: white; + margin-right: 10px; + font-size: 36px; +} + +.RegattaRaceInformation-Header .RaceSeriesAndFleet-Label { + display: inline; + color: white; + font-size: 15px; +} + +.RegattaAndRaceTime-Header { + position: absolute; + right: 10px; + top: 13px; + text-align: right; + color: white; +} + +.RegattaAndRaceTime-Header .RegattaName-Anchor { + font-size: 15px; + color: white; + text-decoration: none; + padding-left: 25px; + background-image: url(images/home.png); + background-repeat: no-repeat; + background-size: 17px; + background-position: 4px 1px; +} + +.RegattaAndRaceTime-Header .RegattaName-Anchor:hover { + text-decoration: underline; +} + +.RegattaAndRaceTime-Header .RaceTime-Label { + font-size: 15px; + margin-top: 5px; +} + +.VideoPopup-Close-Button { + border: none; +} + +.Media-Select-Button { + font-size: 15px; + font-size: 1rem; + display: inline-block; + font-weight: 400; + color: #111; + border: none; + padding: 0.5em 0.3em 0.5em 0.8em; + background-color: #fff; + -webkit-transition: background-color 0.2s; + -moz-transition: background-color 0.2s; + -o-transition: background-color 0.2s; + -ms-transition: background-color 0.2s; + transition: background-color 0.2s; + width: 100%; + max-width: 25em; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.Media-Select-Button:hover, .Media-Select-Button:focus { + background-color: #f0ab00; +} + +.Media-Select-Button-playing { + color: #999; +} + +.Media-Select-Button-playing:hover, .Media-Select-Button-playing:focus { + color: #fff; + background-color: #ccc; +} + +.Media-Select-Popup .dialogMiddleCenterInner { + padding: 0; +} + +.Media-Select-Popup .Caption { + padding: 4px 10px; +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html b/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html new file mode 100644 index 00000000000..440a01f45c2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html @@ -0,0 +1,41 @@ + + + + + + + SAP Sailing Analytics Race Board + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/HomeDesktop.html b/java/com.sap.sailing.gwt.ui/HomeDesktop.html new file mode 100644 index 00000000000..fb2fefff91f --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/HomeDesktop.html @@ -0,0 +1,22 @@ + + + + + + + SAP Sailing - Homepage + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/HomeMobile.html b/java/com.sap.sailing.gwt.ui/HomeMobile.html new file mode 100644 index 00000000000..fb2fefff91f --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/HomeMobile.html @@ -0,0 +1,22 @@ + + + + + + + SAP Sailing - Homepage + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch b/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch index 7c55b6a1b87..3ffcab00446 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch @@ -54,7 +54,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch b/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch index 33e498bd16d..9b40edf5a0f 100644 --- a/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch @@ -1,76 +1,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index 2cd97ea7a01..294ebd7e07c 100644 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -1,76 +1,77 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/build.properties b/java/com.sap.sailing.gwt.ui/build.properties old mode 100755 new mode 100644 index 7b094eba1a3..75618842ba3 --- a/java/com.sap.sailing.gwt.ui/build.properties +++ b/java/com.sap.sailing.gwt.ui/build.properties @@ -48,5 +48,10 @@ bin.includes = META-INF/,\ AutoPlay.html,\ Home.css,\ Home.html,\ - SettingsDialog.css + HomeDesktop.html,\ + HomeMobile.html,\ + SettingsDialog.css,\ + EmbeddedMapAndWindChart.css,\ + EmbeddedMapAndWindChart.html,\ + com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart/ output.. = WEB-INF/classes/ diff --git a/java/com.sap.sailing.gwt.ui/images/home/arrow-down-filled-black.png b/java/com.sap.sailing.gwt.ui/images/home/arrow-down-filled-black.png new file mode 100644 index 00000000000..4f016bdc68a Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/home/arrow-down-filled-black.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/home/arrow-down-filled-yellow.png b/java/com.sap.sailing.gwt.ui/images/home/arrow-down-filled-yellow.png new file mode 100644 index 00000000000..24bc60cc04a Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/home/arrow-down-filled-yellow.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/home/icon-audio.png b/java/com.sap.sailing.gwt.ui/images/home/icon-audio.png new file mode 100644 index 00000000000..5928bcddb43 Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/home/icon-audio.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/home/icon-green-check.svg b/java/com.sap.sailing.gwt.ui/images/home/icon-green-check.svg new file mode 100644 index 00000000000..ef51475e5dd --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/images/home/icon-green-check.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/images/home/icon-video.png b/java/com.sap.sailing.gwt.ui/images/home/icon-video.png new file mode 100644 index 00000000000..ef6c886a6d0 Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/home/icon-video.png differ diff --git a/java/com.sap.sailing.gwt.ui/images/home/icon-wind.png b/java/com.sap.sailing.gwt.ui/images/home/icon-wind.png new file mode 100644 index 00000000000..5cfd89b84da Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/images/home/icon-wind.png differ diff --git a/java/com.sap.sailing.gwt.ui/pom.xml b/java/com.sap.sailing.gwt.ui/pom.xml index f2ac0673da1..4d0430c6c3b 100755 --- a/java/com.sap.sailing.gwt.ui/pom.xml +++ b/java/com.sap.sailing.gwt.ui/pom.xml @@ -130,6 +130,7 @@ com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.RaceBoard + com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.VideoPopup diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.java index 5c6144acb4d..3eee58b4506 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.java @@ -1,10 +1,12 @@ package com.sap.sailing.gwt.home.client.place.event.multiregatta.tabs; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -18,12 +20,22 @@ import com.sap.sailing.gwt.home.client.place.event.multiregatta.EventMultiregatt import com.sap.sailing.gwt.home.client.place.event.multiregatta.MultiregattaTabView; import com.sap.sailing.gwt.home.client.place.event.overview.EventOverviewStage; import com.sap.sailing.gwt.home.client.place.event.partials.eventregatta.EventRegattaList; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.DropdownFilter; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.DropdownFilter.DropdownFilterList; +import com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList.MultiRegattaList; +import com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList.MultiRegattaListStepsLegend; import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLive; import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshManager; +import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshableWidget; +import com.sap.sailing.gwt.home.shared.ExperimentalFeatures; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; import com.sap.sailing.gwt.ui.shared.RaceGroupDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; import com.sap.sailing.gwt.ui.shared.dispatch.event.GetLiveRacesForEventAction; +import com.sap.sailing.gwt.ui.shared.dispatch.event.GetRegattaListViewAction; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; import com.sap.sse.common.Util.Triple; /** @@ -31,10 +43,21 @@ import com.sap.sse.common.Util.Triple; */ public class MultiregattaOverviewTabView extends Composite implements MultiregattaTabView { + interface MyBinder extends UiBinder { + } + + private static MyBinder ourUiBinder = GWT.create(MyBinder.class); + + @UiField SimplePanel content; + @UiField(provided = true) EventOverviewStage stage; + @UiField(provided = true) RacesListLive racesListLive; + @UiField DivElement newContentContainerUi; + @UiField(provided = true) DropdownFilter boatCategoryFilterUi; + @UiField MultiRegattaListStepsLegend regattaProgressLegendUi; + @UiField(provided = true) MultiRegattaList regattaListUi; private Presenter currentPresenter; public MultiregattaOverviewTabView() { - } @Override @@ -54,78 +77,93 @@ public class MultiregattaOverviewTabView extends Composite implements Multiregat @Override public void start(MultiregattaOverviewPlace myPlace, AcceptsOneWidget contentArea) { - racesListLive = new RacesListLive(currentPresenter, true); stage = new EventOverviewStage(currentPresenter); + racesListLive = new RacesListLive(currentPresenter, true); + MultiregattaOverviewRegattasTabViewRegattaFilterList regattaFilterList = new MultiregattaOverviewRegattasTabViewRegattaFilterList(); + boatCategoryFilterUi = new DropdownFilter(StringMessages.INSTANCE.allBoatClasses(), regattaFilterList); + regattaListUi = new MultiRegattaList(currentPresenter); initWidget(ourUiBinder.createAndBindUi(this)); - RefreshManager refreshManager = new RefreshManager(this, currentPresenter.getDispatch()); - stage.setupRefresh(refreshManager); - refreshManager.add(racesListLive, new GetLiveRacesForEventAction(currentPresenter.getCtx().getEventDTO().getId())); + refreshManager.add(racesListLive.getRefreshable(), new GetLiveRacesForEventAction(currentPresenter.getCtx().getEventDTO().getId())); + + if (ExperimentalFeatures.SHOW_NEW_REGATTA_LIST) { + refreshManager.add(regattaFilterList, new GetRegattaListViewAction(currentPresenter.getCtx().getEventDTO().getId())); + content.removeFromParent(); + } else { + boatCategoryFilterUi.removeFromParent(); + regattaProgressLegendUi.removeFromParent(); + regattaListUi.removeFromParent(); + newContentContainerUi.removeFromParent(); + // TODO: understand, and than move this into appropiate place (probably context) + currentPresenter.ensureRegattaStructure(new AsyncCallback>() { + @Override + public void onSuccess(List raceGroups) { + if (raceGroups.size() > 0) { + initView(); + } else { + // createEventWithoutRegattasView(event, panel); + } + } + @Override + public void onFailure(Throwable caught) { + // createErrorView( + // "Error while loading the regatta structure with service getRegattaStructureOfEvent()", + // caught, panel); + } + }); + } contentArea.setWidget(this); + } - // TODO: understand, and than move this into appropiate place (probably context) - currentPresenter.ensureRegattaStructure(new AsyncCallback>() { - @Override - public void onSuccess(List raceGroups) { - if (raceGroups.size() > 0) { - initView(); - } else { - // createEventWithoutRegattasView(event, panel); - } - } + protected void initView() { + Map> regattaStructure = getRegattaStructure(); + EventRegattaList eventRegattaList = new EventRegattaList(regattaStructure, currentPresenter); + content.setWidget(eventRegattaList); + } - @Override - public void onFailure(Throwable caught) { - // createErrorView( - // "Error while loading the regatta structure with service getRegattaStructureOfEvent()", - // caught, panel); - } - }); - } - - protected void initView() { - Map> regattaStructure = getRegattaStructure(); - EventRegattaList eventRegattaList = new EventRegattaList(regattaStructure, currentPresenter); - content.setWidget(eventRegattaList); - } - - private Map> getRegattaStructure() { - Map> result = new HashMap<>(); - Map raceGroupsMap = new HashMap<>(); - for (RaceGroupDTO raceGroup: currentPresenter.getCtx().getRaceGroups()) { - raceGroupsMap.put(raceGroup.getName(), raceGroup); - } - - for (LeaderboardGroupDTO leaderboardGroup : currentPresenter.getCtx().getLeaderboardGroups()) { - for(StrippedLeaderboardDTO leaderboard: leaderboardGroup.getLeaderboards()) { - String leaderboardName = leaderboard.name; - result.put(leaderboardName, new Triple(raceGroupsMap.get(leaderboardName), - leaderboard, leaderboardGroup)); - } - } - return result; - } + private Map> getRegattaStructure() { + Map> result = new HashMap<>(); + Map raceGroupsMap = new HashMap<>(); + for (RaceGroupDTO raceGroup : currentPresenter.getCtx().getRaceGroups()) { + raceGroupsMap.put(raceGroup.getName(), raceGroup); + } + for (LeaderboardGroupDTO leaderboardGroup : currentPresenter.getCtx().getLeaderboardGroups()) { + for (StrippedLeaderboardDTO leaderboard : leaderboardGroup.getLeaderboards()) { + String leaderboardName = leaderboard.name; + result.put(leaderboardName, new Triple( + raceGroupsMap.get(leaderboardName), leaderboard, leaderboardGroup)); + } + } + return result; + } @Override public void stop() { } - - interface MyBinder extends UiBinder { - } - - private static MyBinder ourUiBinder = GWT.create(MyBinder.class); - - @UiField SimplePanel content; - @UiField(provided = true) RacesListLive racesListLive; - @UiField(provided = true) EventOverviewStage stage; - @Override public MultiregattaOverviewPlace placeToFire() { return new MultiregattaOverviewPlace(currentPresenter.getCtx()); } + + private class MultiregattaOverviewRegattasTabViewRegattaFilterList implements + DropdownFilterList, RefreshableWidget> { + @Override + public void setData(SortedSetResult data, long nextUpdate, int updateNo) { + regattaListUi.setData(data, nextUpdate, updateNo); + boatCategoryFilterUi.updateFilterValues(); + } + @Override + public Collection getSelectableValues() { + return regattaListUi.getSelectableBoatCategories(); + } + @Override + public void onSelectFilter(String value) { + regattaListUi.setVisibleBoatCategory(value); + } + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.ui.xml index 7e73a5c2923..5579f0e180b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaOverviewTabView.ui.xml @@ -1,13 +1,27 @@ + + .legend_and_filter { + text-align: right; + } + .legend_and_filter > div { + display: inline-block; + vertical-align: middle; + } + -
-
- +
+
+ +
+ + +
+
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.java index 9d2ef2fee2d..2162f1f7626 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.java @@ -1,10 +1,12 @@ package com.sap.sailing.gwt.home.client.place.event.multiregatta.tabs; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -17,12 +19,22 @@ import com.sap.sailing.gwt.home.client.place.event.multiregatta.EventMultiregatt import com.sap.sailing.gwt.home.client.place.event.multiregatta.EventMultiregattaView.Presenter; import com.sap.sailing.gwt.home.client.place.event.multiregatta.MultiregattaTabView; import com.sap.sailing.gwt.home.client.place.event.partials.eventregatta.EventRegattaList; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.DropdownFilter; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.DropdownFilter.DropdownFilterList; +import com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList.MultiRegattaList; +import com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList.MultiRegattaListStepsLegend; import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLive; import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshManager; +import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshableWidget; +import com.sap.sailing.gwt.home.shared.ExperimentalFeatures; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; import com.sap.sailing.gwt.ui.shared.RaceGroupDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; import com.sap.sailing.gwt.ui.shared.dispatch.event.GetLiveRacesForEventAction; +import com.sap.sailing.gwt.ui.shared.dispatch.event.GetRegattaListViewAction; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; import com.sap.sse.common.Util.Triple; /** @@ -30,12 +42,20 @@ import com.sap.sse.common.Util.Triple; */ public class MultiregattaRegattasTabView extends Composite implements MultiregattaTabView { + interface MyBinder extends UiBinder { + } + + private static MyBinder ourUiBinder = GWT.create(MyBinder.class); + @UiField SimplePanel content; - @UiField(provided = true) RacesListLive racesListLive; + @UiField DivElement newContentContainerUi; + @UiField(provided = true) RacesListLive racesListLiveUi; + @UiField(provided = true) DropdownFilter boatCategoryFilterUi; + @UiField MultiRegattaListStepsLegend regattaProgressLegendUi; + @UiField(provided = true) MultiRegattaList regattaListUi; private Presenter currentPresenter; public MultiregattaRegattasTabView() { - } @Override @@ -55,32 +75,45 @@ public class MultiregattaRegattasTabView extends Composite implements Multiregat @Override public void start(final MultiregattaRegattasPlace myPlace, final AcceptsOneWidget contentArea) { - racesListLive = new RacesListLive(currentPresenter, true); + racesListLiveUi = new RacesListLive(currentPresenter, true); + MultiregattaRegattasTabViewRegattaFilterList regattaFilterList = new MultiregattaRegattasTabViewRegattaFilterList(); + boatCategoryFilterUi = new DropdownFilter(StringMessages.INSTANCE.allBoatClasses(), regattaFilterList); + regattaListUi = new MultiRegattaList(currentPresenter); + initWidget(ourUiBinder.createAndBindUi(this)); RefreshManager refreshManager = new RefreshManager(this, currentPresenter.getDispatch()); - refreshManager.add(racesListLive, new GetLiveRacesForEventAction(currentPresenter.getCtx().getEventDTO().getId())); - contentArea.setWidget(this); + refreshManager.add(racesListLiveUi.getRefreshable(), new GetLiveRacesForEventAction(currentPresenter.getCtx().getEventDTO().getId())); - // TODO: understand, and than move this into appropiate place (probably context) - currentPresenter.ensureRegattaStructure(new AsyncCallback>() { - @Override - public void onSuccess(List raceGroups) { - if (raceGroups.size() > 0) { - initView(); - } else { - // createEventWithoutRegattasView(event, panel); - } + if (ExperimentalFeatures.SHOW_NEW_REGATTA_LIST) { + refreshManager.add(regattaFilterList, new GetRegattaListViewAction(currentPresenter.getCtx().getEventDTO().getId())); + content.removeFromParent(); + } else { + boatCategoryFilterUi.removeFromParent(); + regattaProgressLegendUi.removeFromParent(); + regattaListUi.removeFromParent(); + newContentContainerUi.removeFromParent(); + // TODO: understand, and than move this into appropiate place (probably context) + currentPresenter.ensureRegattaStructure(new AsyncCallback>() { + @Override + public void onSuccess(List raceGroups) { + if (raceGroups.size() > 0) { + initView(); + } else { + // createEventWithoutRegattasView(event, panel); } + } - @Override - public void onFailure(Throwable caught) { - // createErrorView( - // "Error while loading the regatta structure with service getRegattaStructureOfEvent()", - // caught, panel); - } - }); + @Override + public void onFailure(Throwable caught) { + // createErrorView( + // "Error while loading the regatta structure with service getRegattaStructureOfEvent()", + // caught, panel); + } + }); + } + contentArea.setWidget(this); } - + protected void initView() { Map> regattaStructure = getRegattaStructure(); EventRegattaList eventRegattaList = new EventRegattaList(regattaStructure, currentPresenter); @@ -106,17 +139,32 @@ public class MultiregattaRegattasTabView extends Composite implements Multiregat @Override public void stop() { - } - interface MyBinder extends UiBinder { - } - - private static MyBinder ourUiBinder = GWT.create(MyBinder.class); - @Override public MultiregattaRegattasPlace placeToFire() { return new MultiregattaRegattasPlace(currentPresenter.getCtx()); } + + private class MultiregattaRegattasTabViewRegattaFilterList implements + DropdownFilterList, RefreshableWidget> { + + @Override + public void setData(SortedSetResult data, long nextUpdate, int updateNo) { + regattaListUi.setData(data, nextUpdate, updateNo); + boatCategoryFilterUi.updateFilterValues(); + } + + @Override + public Collection getSelectableValues() { + return regattaListUi.getSelectableBoatCategories(); + } + + @Override + public void onSelectFilter(String value) { + regattaListUi.setVisibleBoatCategory(value); + } + + } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.ui.xml index 581f562df2b..b12d3120001 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/multiregatta/tabs/MultiregattaRegattasTabView.ui.xml @@ -1,19 +1,27 @@ - - - - .space-top { - margin-top: 2em; - margin-bottom: 2em; + + + + .legend_and_filter { + text-align: right; } - - -
-
- -
-
- -
+ .legend_and_filter > div { + display: inline-block; + vertical-align: middle; + } +
+ +
+
+ +
+ + +
+ +
+
+ +
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.java index 7969d96691c..c9f1b8ccbf1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.java @@ -41,6 +41,8 @@ public class EventHeader extends Composite { @UiField StringMessages i18n; @UiField DivElement eventLogo; + @UiField + AnchorElement eventLogoAnchorUi; @UiField HeadingElement staticTitle; @UiField SpanElement eventName; @UiField DivElement eventState; @@ -56,6 +58,7 @@ public class EventHeader extends Composite { @UiField DivElement races; @UiField DivElement trackedRaces; @UiField DivElement eventCategory; + @UiField DivElement courseAreaUi; @UiField FlowPanel dropdownContent; @@ -96,7 +99,9 @@ public class EventHeader extends Composite { private void initFields() { LogoUtil.setEventLogo(eventLogo, event); - + if (presenter.showRegattaMetadata()) { + presenter.getCurrentEventNavigation().configureAnchorElement(eventLogoAnchorUi); + } String eventDisplayName = event.getDisplayName(); String nameToShow; if(presenter.showRegattaMetadata()) { @@ -123,6 +128,11 @@ public class EventHeader extends Composite { } else { hide(trackedRaces); } + if(regattaMetadata.getDefaultCourseAreaName() != null) { + courseAreaUi.setInnerText(i18n.courseAreaName(regattaMetadata.getDefaultCourseAreaName())); + } else { + hide(courseAreaUi); + } if(regattaMetadata.getBoatCategory() != null) { eventCategory.setInnerText(regattaMetadata.getBoatCategory()); } else { @@ -149,7 +159,7 @@ public class EventHeader extends Composite { hide(eventLink); } - hide(competitors, races, trackedRaces); + hide(competitors, races, trackedRaces, courseAreaUi, eventCategory); } initTitleAndSelection(nameToShow); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.ui.xml index 4c94e3af3bd..a5afb03d77f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/header/EventHeader.ui.xml @@ -10,7 +10,7 @@
-
+
@@ -53,9 +53,8 @@
-
- -
+
+
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/DropdownFilter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/DropdownFilter.java new file mode 100644 index 00000000000..725499053f8 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/DropdownFilter.java @@ -0,0 +1,112 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.listNavigation; + +import java.util.Collection; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.common.client.LinkUtil; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.RegattaNavigationResources.LocalCss; +import com.sap.sailing.gwt.home.client.shared.DropdownHandler; + +public class DropdownFilter extends Composite { + + private static final LocalCss CSS = RegattaNavigationResources.INSTANCE.css(); + private static ListDropdownFilterUiBinder uiBinder = GWT.create(ListDropdownFilterUiBinder.class); + + interface ListDropdownFilterUiBinder extends UiBinder> { + } + + @UiField DivElement dropdownContainerUi; + @UiField DivElement currentValueUi; + @UiField FlowPanel filterItemContainerUi; + private final DropdownFilterList filterList; + private final DropdownHandler dropdownHandler; + private final String nullValueLabel; + private T selectedFilterValue = null; + + public DropdownFilter(String nullValueLabel, DropdownFilterList filterList) { + this.nullValueLabel = nullValueLabel; + this.filterList = filterList; + CSS.ensureInjected(); + initWidget(uiBinder.createAndBindUi(this)); + this.dropdownHandler = new ListFilterDropdownHandler(); + } + + public void updateFilterValues() { + filterItemContainerUi.clear(); + DropdownFilterItem filterItemToSelect = addFilterValue(null, nullValueLabel); + for (T value : filterList.getSelectableValues()) { + DropdownFilterItem newFilterItem = addFilterValue(value, String.valueOf(value)); + filterItemToSelect = value.equals(selectedFilterValue) ? newFilterItem : filterItemToSelect; + } + filterItemToSelect.select(); + setVisible(filterList.getSelectableValues().size() > 1); + } + + private DropdownFilterItem addFilterValue(T value, String label) { + DropdownFilterItem filterItem = new DropdownFilterItem(value, label); + filterItemContainerUi.add(filterItem); + return filterItem; + } + + public interface DropdownFilterList { + Collection getSelectableValues(); + void onSelectFilter(T value); + } + + private class ListFilterDropdownHandler extends DropdownHandler { + private ListFilterDropdownHandler() { + super(dropdownContainerUi, filterItemContainerUi.getElement()); + } + @Override + protected void dropdownStateChanged(boolean dropdownShown) { + if (dropdownShown) dropdownContainerUi.addClassName(CSS.jsdropdownactive()); + else dropdownContainerUi.removeClassName(CSS.jsdropdownactive()); + } + } + + private class DropdownFilterItem extends Widget { + private final T value; + private final String label; + private DropdownFilterItem(T value, String label) { + this.value = value; + this.label = label; + this.setElement(DOM.createDiv().cast()); + this.getElement().setInnerText(label); + this.addStyleName(CSS.regattanavigation_filter_dropdown_link()); + this.initClickHandler(); + } + + private void initClickHandler() { + this.addDomHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if(LinkUtil.handleLinkClick((Event) event.getNativeEvent())) { + event.preventDefault(); + DropdownFilterItem.this.select(); + DropdownFilter.this.dropdownHandler.setVisible(false); + } + } + }, ClickEvent.getType()); + } + + private void select() { + DropdownFilter.this.selectedFilterValue = value; + DropdownFilter.this.currentValueUi.setInnerText(label); + for (int i=0; i < filterItemContainerUi.getWidgetCount(); i++) { + filterItemContainerUi.getWidget(i).removeStyleName(CSS.regattanavigation_filter_dropdown_linkactive()); + } + this.addStyleName(CSS.regattanavigation_filter_dropdown_linkactive()); + DropdownFilter.this.filterList.onSelectFilter(value); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/DropdownFilter.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/DropdownFilter.ui.xml new file mode 100644 index 00000000000..e188cd37436 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/DropdownFilter.ui.xml @@ -0,0 +1,18 @@ + + + + + + + +
+
+
+ : +
+
+
+ +
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/ListNavigationPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/ListNavigationPanel.java new file mode 100644 index 00000000000..db818343b55 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/ListNavigationPanel.java @@ -0,0 +1,85 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.listNavigation; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.ListNavigationPanel.ListNavigationAction; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.RegattaNavigationResources.LocalCss; + +public class ListNavigationPanel extends Composite { + + private static final LocalCss CSS = RegattaNavigationResources.INSTANCE.css(); + + private static ListNavigationPanelUiBinder uiBinder = GWT.create(ListNavigationPanelUiBinder.class); + + interface ListNavigationPanelUiBinder extends UiBinder> { + } + + @UiField FlowPanel actionContainerUi; + @UiField SimplePanel additionalWidgetContainerUi; + + private SelectionCallback selectionCallback; + + public ListNavigationPanel(SelectionCallback selectionCallback) { + this.selectionCallback = selectionCallback; + CSS.ensureInjected(); + initWidget(uiBinder.createAndBindUi(this)); + } + + public void addAction(final T action, boolean select) { + ActionWidget actionWidget = new ActionWidget(action); + actionContainerUi.add(actionWidget); + if (select) { + actionWidget.select(); + } + } + + public void setAdditionalWidget(Widget additionalWidget) { + this.additionalWidgetContainerUi.setWidget(additionalWidget); + } + + public interface SelectionCallback { + void onSelectAction(T action); + } + + public interface ListNavigationAction { + String getDisplayName(); + boolean isShowAdditionalWidget(); + } + + private class ActionWidget extends Label { + private final T action; + private final Display displayStyle; + + private ActionWidget(T action) { + super(action.getDisplayName()); + this.action = action; + this.displayStyle = action.isShowAdditionalWidget() ? Display.BLOCK : Display.NONE; + this.addStyleName(CSS.regattanavigation_button()); + this.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + ActionWidget.this.select(); + } + }); + } + + private void select() { + additionalWidgetContainerUi.getElement().getStyle().setDisplay(displayStyle); + for (int i=0; i < actionContainerUi.getWidgetCount(); i++) { + actionContainerUi.getWidget(i).removeStyleName(CSS.regattanavigation_buttonactive()); + } + this.addStyleName(CSS.regattanavigation_buttonactive()); + selectionCallback.onSelectAction(action); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/ListNavigationPanel.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/ListNavigationPanel.ui.xml new file mode 100644 index 00000000000..b9c111a77e6 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/ListNavigationPanel.ui.xml @@ -0,0 +1,15 @@ + + + + + + + + + + : + + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RaceStateLegend.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RaceStateLegend.java new file mode 100644 index 00000000000..50cae03813c --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RaceStateLegend.java @@ -0,0 +1,19 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.listNavigation; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.user.client.ui.Widget; + +public class RaceStateLegend extends Widget { + + private static RaceStateLegendUiBinder uiBinder = GWT.create(RaceStateLegendUiBinder.class); + + interface RaceStateLegendUiBinder extends UiBinder { + } + + public RaceStateLegend() { + setElement(uiBinder.createAndBindUi(this)); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RaceStateLegend.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RaceStateLegend.ui.xml new file mode 100644 index 00000000000..e5449e23890 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RaceStateLegend.ui.xml @@ -0,0 +1,21 @@ + + + + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RegattaNavigation.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RegattaNavigation.gss new file mode 100644 index 00000000000..83c96c74862 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RegattaNavigation.gss @@ -0,0 +1,148 @@ +/** + * A toggle buttons and legend for regatta races views + */ +.jsdropdown { + position: relative; +} +.jsdropdown_head { + padding-right: 2em; +} +.jsdropdown_head, +.jsdropdown_content { + text-indent: 0; +} +.regattanavigation { + font-size: 1rem; + margin-top: 1em; + margin-bottom: 1em; +} +.regattanavigation_text { + font-size: 0.866666666666667rem; + line-height: 2.307692307692307em; + margin-right: 0.769230769230769em; + display: inline-block; + color: #999; +} +.regattanavigation_button { + display: inline-block; + font-weight: 600; + padding: 0.333333333333333em 0.666666666666667em; + line-height: 1.333333333333333em; + margin-right: 0.666666666666667em; + color: #666; + background-color: #f2f2f2; + -webkit-transition: all 0.2s; + -moz-transition: all 0.2s; + transition: all 0.2s; + cursor: pointer; +} +.regattanavigation_button:hover { + text-decoration: none; + color: #fff; + background-color: #efaa00; +} +.regattanavigation_buttonactive { + color: #fff; + background-color: #efaa00; +} +.regattanavigation_filter { + font-size: 0.866666666666667rem; + text-align: right; + margin-top: 0; + background: transparent url("images/home/arrow-down-filled-black.png") no-repeat; + background-position: 96%; + background-size: 0.923076923076923em 0.538461538461538em; + border: 1px solid transparent; + border-radius: 0.333333333333333em 0.333333333333333em 0 0; + position: relative; + float: right; + cursor: pointer; + padding-left: 0.5em; +} +.regattanavigation_filter:hover, +.regattanavigation_filter.jsdropdownactive { + border-color: #dedede; + background-color: #f2f2f2; +} +.regattanavigation_filter.jsdropdownactive { + background-image: url("images/home/arrow-down-filled-yellow.png"); +} +.regattanavigation_filter.jsdropdownactive .regattanavigation_filter_dropdown { + display: block; +} +.regattanavigation_filter_text, +.regattanavigation_filter_current { + display: inline-block; + line-height: 2.307692307692307em; +} +.regattanavigation_filter_text { + color: #999; +} +.regattanavigation_filter_dropdown { + display: none; + position: absolute; + right: -1px; + background: #fff; + border: 1px solid #dedede; + line-height: 2.307692307692307em; + z-index: 10; + width: 110%; +} +.regattanavigation_filter_dropdown_link { + display: block; + color: #999; + text-align: left; + padding: 0 2.307692307692307em 0 0.769230769230769em; +} +.regattanavigation_filter_dropdown_link:hover { + text-decoration: underline; +} +.regattanavigation_filter_dropdown_linkactive { + background: url("images/home/dropdown__check@2x.png") no-repeat; + background-position: 95%; + background-size: 1.2em 0.933333333333333em; +} +.regattanavigation_legend { + font-size: 1rem; + width: 100%; + margin-top: 1.333333333333333em; +} +@media (min-width: 61em) { + .regattanavigation_legend { + text-align: right; + margin-top: 0; + } + .regattanavigation_legend_item { + margin-right: 0; + margin-left: 0.666666666666667em; + } +} +.regattanavigation_legend_item { + display: inline-block; + padding: 0.4em 0.666666666666667em; + margin: 0.333333333333333em 0.666666666666667em 0.333333333333333em 0; + font-size: 0.666em; + font-weight: 700; + line-height: 0.666666666666667em; + border: 1px solid #008fcc; +} +.regattanavigation_legend_itemtracked { + color: #fff; + border-color: #008fcc; + background-color: #008fcc; +} +.regattanavigation_legend_itemuntracked { + color: #fff; + border-color: #ccc; + background-color: #666; +} +.regattanavigation_legend_itemlive { + color: #fff; + border-color: #e94a1b; + background-color: #e94a1b; +} +.regattanavigation_legend_itemplanned { + color: #ccc; + border-color: #ccc; + background-color: #fff; +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RegattaNavigationResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RegattaNavigationResources.java new file mode 100644 index 00000000000..f4aa7eb4748 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/listNavigation/RegattaNavigationResources.java @@ -0,0 +1,35 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.listNavigation; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.CssResource; + +public interface RegattaNavigationResources extends ClientBundle { + public static final RegattaNavigationResources INSTANCE = GWT.create(RegattaNavigationResources.class); + + @Source("RegattaNavigation.gss") + LocalCss css(); + + public interface LocalCss extends CssResource { + String jsdropdown(); + String jsdropdown_head(); + String jsdropdown_content(); + String regattanavigation(); + String regattanavigation_text(); + String regattanavigation_button(); + String regattanavigation_buttonactive(); + String regattanavigation_filter(); + String jsdropdownactive(); + String regattanavigation_filter_dropdown(); + String regattanavigation_filter_text(); + String regattanavigation_filter_current(); + String regattanavigation_filter_dropdown_link(); + String regattanavigation_filter_dropdown_linkactive(); + String regattanavigation_legend(); + String regattanavigation_legend_item(); + String regattanavigation_legend_itemtracked(); + String regattanavigation_legend_itemuntracked(); + String regattanavigation_legend_itemlive(); + String regattanavigation_legend_itemplanned(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.gss new file mode 100644 index 00000000000..42b617a51be --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.gss @@ -0,0 +1,255 @@ +/** + * A list of regattas to an event + */ +.race { + color: #333; + font-size: 1rem; + overflow: hidden; +} +.race:last-of-type .race_item { + border: none; +} +.race_fleetcorner { + width: 1em; + vertical-align: top; + border-bottom: 1px solid #ebebeb; +} +.race_fleetcorner_icon { + width: 1em; + margin-right: -2.333333333333333em; + position: relative; + top: 1px; + left: 1px; + border-style: solid; + border-color: transparent; + border-width: 1em 1em 0 0; +} +.race_item { + text-align: left; + vertical-align: baseline; + border-bottom: 1px solid #ebebeb; + white-space: nowrap; + padding: 1.4em 0.666666666666667em 0.733333333333333em 1.333333333333333em; +} +.race_item .button { + white-space: nowrap; +} +.race_item:first-of-type { + padding-left: 2em; +} +.race_itemrank:after { + content: '.'; +} +.race_itemshort { + width: 5em; +} +.race_itemlong { + width: 32.5%; +} +.race_itemwind { + white-space: nowrap; +} +.race_itemwindflag { + padding: 1.066666666666667em 0.666666666666667em 0.4em 1.333333333333333em; +} +img.race_item_windflag { + max-width: 2.4em; + max-height: 2.4em; + vertical-align: middle; +} +.race_itemname { + font-weight: 600; +} +.race_itemwinner { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + vertical-align: middle; + min-width: 13.333333333333334em; + max-width: 16.666666666666668em; +} +.race_item_flag { + max-width: 1.6em; + max-height: 1.6em; + vertical-align: middle; +} +.race_item_position { + margin-right: 0.333333333333333em; +} +.race_item_winner { + margin-left: 5px; + vertical-align: middle; +} +.race_itemwinner { + padding: 0.733333333333333em 1.333333333333333em; +} +.race_itemcenter { + text-align: center; +} +.race_itemright { + text-align: right; + vertical-align: middle; + padding-left: 0; + padding-right: 0.666666666666667em; + padding-top: 0.666666666666667em; +} +.race_itemright_button { + padding: 0.666666666666667em; +} +.race_itemstatus { + position: relative; +} +.race_itemstatus:first-letter { + text-transform: capitalize; +} +.race_itemstatus_container { + min-width: 5.333333333333333em; + display: inline-block; +} +.race_itemstatus_container_text { + clip: auto; +} +.race_itemstatus_container_progressbar { + height: 0.2em; + position: relative; + top: 0.2em; + width: 100%; + background-color: #dedede; +} +.race_itemstatus_container_progressbar_progress { + height: 100%; + background-color: #efaa00; +} +.racewinners .race_item { + padding: 1.2em 0.666666666666667em 1.2em 1.333333333333333em; +} +.racewinners .race_item:nth-last-child(1) { + display: none; +} +@media (min-width: 61em) { + .racewinners .race_item:nth-last-child(1) { + display: inline-block; + } +} +.racewinners .race_itemname { + vertical-align: middle; +} +.race_fleetcornerblue:before { + border-top-color: #00f; +} +.race_fleetcornerred:before { + border-top-color: #f00; +} +.race_fleetcornersilver:before { + border-top-color: #c0c0c0; +} +.race_fleetcornergold:before { + border-top-color: #ffd700; +} +.race_fleetcorneryellow:before { + border-top-color: #ff0; +} +.regattalistitem { + font-size: 1rem; + float: left; + width: 100%; + margin-top: 2em; + border: 1px solid #ddd; + border-radius: 0.333333333333333em; + overflow: hidden; +} +.regattalistitem > div:last-child { + border-bottom: none; +} +.regattalistitem_table { + width: 100%; + border-spacing: 0; + border-collapse: separate; + border-bottom: 1px solid #ddd; +} +.regattalistitem_table:last-of-type { + border-bottom: none; +} +.regattalistitem_tableborderTop { + border-top: 1px solid #ebebeb; +} +.regattalistitem_steps { + border-bottom: 1px solid #ddd; + overflow: hidden; +} +.regattalistitem_steps_step { + float: left; + position: relative; + height: 4em; + text-transform: uppercase; + padding: 0 0.666666666666667em; +} +.regattalistitem_steps_stepinactive { + color: #ddd; +} +.regattalistitem_steps_step_infowrapper { + position: relative; + top: 50%; + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + transform: translateY(-50%); + white-space: nowrap; +} +.regattalistitem_steps_step_name { + font-weight: 600; + display: inline-block; + margin-right: 0.333333333333333em; + font-size: 0; +} +.regattalistitem_steps_step_name::first-letter { + font-size: 0.866666666666667rem; + margin-right: 0.333333333333333rem; +} +@media (min-width: 49.0625em) { + .regattalistitem_steps_step_name::first-letter { + margin-right: 0; + } +} +@media (min-width: 49.0625em) { + .regattalistitem_steps_step_name { + font-size: 0.866666666666667em; + } +} +.regattalistitem_steps_step_check { + display: inline-block; + vertical-align: middle; + width: 1.066666666666667em; + height: 1.066666666666667em; + background-image: url("images/home/icon-green-check.svg"); + background-repeat: no-repeat; + background-size: contain; + margin-right: 0.333333333333333em; +} +.regattalistitem_steps_step_progress { + display: inline-block; + font-size: 0.866666666666667em; + text-transform: none; +} +.regattalistitem_steps_step_fleets { + width: 100%; + height: 17px; + outline: 1px solid #dedede; + margin-top: 0.266666666666667em; + padding: 1px; +} +.regattalistitem_steps_step_fleets_fleet { + font-size: inherit; +} +.regattalistitem_steps_step_fleets_fleet_progress { + height: 100%; +} +.regattalistitem_steps_button { + height: 100%; + padding: 0.666666666666667em; + text-align: right; + margin-top: 3px; +} +.regattalistitem_steps_button .button { + margin-left: 0.8em; +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.java new file mode 100644 index 00000000000..999216c7de5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.java @@ -0,0 +1,62 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import java.util.Collection; +import java.util.Collections; +import java.util.TreeSet; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.home.client.place.event.multiregatta.EventMultiregattaView.Presenter; +import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshableWidget; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; + +public class MultiRegattaList extends Composite implements RefreshableWidget> { + + private static MultiRegattaListUiBinder uiBinder = GWT.create(MultiRegattaListUiBinder.class); + + interface MultiRegattaListUiBinder extends UiBinder { + } + + @UiField FlowPanel regattasContainerUi; + private final Presenter currentPresenter; + private final Collection selectableBoatCategories = new TreeSet<>(); + + public MultiRegattaList(Presenter presenter) { + this.currentPresenter = presenter; + initWidget(uiBinder.createAndBindUi(this)); + } + + @Override + public void setData(SortedSetResult data, long nextUpdate, int updateNo) { + this.setListData(data == null ? Collections.emptySet() : data.getValues()); + } + + public void setListData(Collection data) { + regattasContainerUi.clear(); + selectableBoatCategories.clear(); + for (RegattaWithProgressDTO regattaWithProgress : data) { + regattasContainerUi.add(new MultiRegattaListItem(regattaWithProgress, currentPresenter)); + String boatCategory = regattaWithProgress.getBoatCategory(); + if(boatCategory != null) { + selectableBoatCategories.add(boatCategory); + } + } + } + + public Collection getSelectableBoatCategories() { + return selectableBoatCategories; + } + + public void setVisibleBoatCategory(String visibleBoatCategory) { + for (int i=0; i < regattasContainerUi.getWidgetCount(); i++) { + MultiRegattaListItem item = (MultiRegattaListItem) regattasContainerUi.getWidget(i); + item.setVisibilityDependingOnBoatCategory(visibleBoatCategory); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.ui.xml new file mode 100644 index 00000000000..89467cba98b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaList.ui.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListItem.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListItem.java new file mode 100644 index 00000000000..3418af5c0a4 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListItem.java @@ -0,0 +1,42 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.home.client.place.event.multiregatta.EventMultiregattaView.Presenter; +import com.sap.sailing.gwt.home.client.place.event.partials.regattaHeader.RegattaHeader; +import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; + +public class MultiRegattaListItem extends Composite { + + private static MultiRegattaListItemUiBinder uiBinder = GWT.create(MultiRegattaListItemUiBinder.class); + + interface MultiRegattaListItemUiBinder extends UiBinder { + } + + @UiField(provided = true) RegattaHeader regattaHeaderUi; + @UiField(provided = true) MultiRegattaListSteps regattaStepsUi; + private final RegattaWithProgressDTO regattaWithProgress; + + public MultiRegattaListItem(RegattaWithProgressDTO regattaWithProgress) { + this.regattaWithProgress = regattaWithProgress; + regattaHeaderUi = new RegattaHeader(regattaWithProgress); + regattaStepsUi = new MultiRegattaListSteps(regattaWithProgress.getProgress()); + initWidget(uiBinder.createAndBindUi(this)); + } + + public MultiRegattaListItem(RegattaWithProgressDTO regattaWithProgress, Presenter presenter) { + this(regattaWithProgress); + regattaHeaderUi.setRegattaNavigation(presenter.getRegattaNavigation(regattaWithProgress.getId())); + PlaceNavigation leaderboardNavigation = presenter.getRegattaLeaderboardNavigation(regattaWithProgress.getId()); + regattaStepsUi.setLeaderboardNavigation(regattaWithProgress.getState(), leaderboardNavigation); + } + + void setVisibilityDependingOnBoatCategory(String visibleBoatCategory) { + setVisible(visibleBoatCategory == null || visibleBoatCategory.equals(regattaWithProgress.getBoatCategory())); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListItem.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListItem.ui.xml new file mode 100644 index 00000000000..0b6bf8c1799 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListItem.ui.xml @@ -0,0 +1,18 @@ + + + + + + +
+ + + +
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListResources.java new file mode 100644 index 00000000000..4135f335be9 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListResources.java @@ -0,0 +1,59 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.CssResource; + +public interface MultiRegattaListResources extends ClientBundle { + public static final MultiRegattaListResources INSTANCE = GWT.create(MultiRegattaListResources.class); + + @Source("MultiRegattaList.gss") + LocalCss css(); + + public interface LocalCss extends CssResource { + String race(); + String race_item(); + String race_fleetcorner(); + String race_fleetcorner_icon(); + String button(); + String race_itemrank(); + String race_itemshort(); + String race_itemlong(); + String race_itemwind(); + String race_itemwindflag(); + String race_item_windflag(); + String race_itemname(); + String race_itemwinner(); + String race_item_flag(); + String race_item_position(); + String race_item_winner(); + String race_itemcenter(); + String race_itemright(); + String race_itemright_button(); + String race_itemstatus(); + String race_itemstatus_container(); + String race_itemstatus_container_text(); + String race_itemstatus_container_progressbar(); + String race_itemstatus_container_progressbar_progress(); + String racewinners(); + String race_fleetcornerblue(); + String race_fleetcornerred(); + String race_fleetcornersilver(); + String race_fleetcornergold(); + String race_fleetcorneryellow(); + String regattalistitem(); + String regattalistitem_table(); + String regattalistitem_tableborderTop(); + String regattalistitem_steps(); + String regattalistitem_steps_step(); + String regattalistitem_steps_stepinactive(); + String regattalistitem_steps_step_infowrapper(); + String regattalistitem_steps_step_name(); + String regattalistitem_steps_step_check(); + String regattalistitem_steps_step_progress(); + String regattalistitem_steps_step_fleets(); + String regattalistitem_steps_step_fleets_fleet(); + String regattalistitem_steps_step_fleets_fleet_progress(); + String regattalistitem_steps_button(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListSteps.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListSteps.java new file mode 100644 index 00000000000..98f9d0e6fdc --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListSteps.java @@ -0,0 +1,100 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.AnchorElement; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.logical.shared.ResizeEvent; +import com.google.gwt.event.logical.shared.ResizeHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.common.client.SharedResources; +import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressSeriesDTO; +import com.sap.sailing.gwt.ui.shared.eventview.HasRegattaMetadata.RegattaState; + +public class MultiRegattaListSteps extends Composite { + + private static MultiRegattaListStepsUiBinder uiBinder = GWT.create(MultiRegattaListStepsUiBinder.class); + + interface MultiRegattaListStepsUiBinder extends UiBinder { + } + + @UiField DivElement progressContainerUi; + @UiField DivElement stepsContainerUi; + @UiField DivElement leaderboardButtonContainerUi; + @UiField AnchorElement leaderboardButtonUi; + + private HandlerRegistration windowResizeHandlerRegistration; + private final List allMultiregattaSteps = new ArrayList(); + + public MultiRegattaListSteps(RegattaProgressDTO regattaProgress) { + MultiRegattaListResources.INSTANCE.css().ensureInjected(); + initWidget(uiBinder.createAndBindUi(this)); + double sumParts = 0; + for(RegattaProgressSeriesDTO seriesProgress : regattaProgress.getSeries()) { + double parts = Math.pow(seriesProgress.getTotalRaceCount(), 0.4); + sumParts += parts; + } + for(RegattaProgressSeriesDTO seriesProgress : regattaProgress.getSeries()) { + double parts = Math.pow(seriesProgress.getTotalRaceCount(), 0.4); + double percentage = 100.0 * parts / sumParts; + MultiRegattaListStepsBody stepsBody = new MultiRegattaListStepsBody(seriesProgress); + allMultiregattaSteps.add(stepsBody); + stepsBody.getElement().getStyle().setWidth(percentage, Unit.PCT); + stepsContainerUi.appendChild(stepsBody.getElement()); + } + + } + + @Override + protected void onLoad() { + windowResizeHandlerRegistration = Window.addResizeHandler(new ResizeHandler() { + @Override + public void onResize(ResizeEvent event) { + for (MultiRegattaListStepsBody widget : allMultiregattaSteps) { + widget.onResize(); + } + } + }); + } + + @Override + protected void onUnload() { + windowResizeHandlerRegistration.removeHandler(); + } + + void setLeaderboardNavigation(RegattaState regattaState, PlaceNavigation placeNavigation) { + placeNavigation.configureAnchorElement(leaderboardButtonUi); + leaderboardButtonUi.getStyle().clearDisplay(); + leaderboardButtonUi.addClassName(getLeaderboardButtonStyle(regattaState)); + stepsContainerUi.getStyle().setDisplay(Display.NONE); + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + stepsContainerUi.getStyle().clearDisplay(); + stepsContainerUi.getStyle().setMarginRight(leaderboardButtonUi.getOffsetWidth() + 20, Unit.PX); + } + }); + } + + private String getLeaderboardButtonStyle(RegattaState regattaState) { + switch (regattaState) { + case UPCOMING: return SharedResources.INSTANCE.mainCss().buttoninactive(); + case RUNNING: return SharedResources.INSTANCE.mainCss().buttonred(); + default: return SharedResources.INSTANCE.mainCss().buttonprimary(); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListSteps.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListSteps.ui.xml new file mode 100644 index 00000000000..355541e4715 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListSteps.ui.xml @@ -0,0 +1,18 @@ + + + + + + +
+
+
+ +
+
+ + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBody.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBody.java new file mode 100644 index 00000000000..7afd6f6ff85 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBody.java @@ -0,0 +1,89 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import static com.sap.sailing.domain.common.LeaderboardNameConstants.DEFAULT_SERIES_NAME; + +import java.util.Map; +import java.util.Map.Entry; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.dom.client.Style.Visibility; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.RequiresResize; +import com.google.gwt.user.client.ui.UIObject; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressFleetDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressSeriesDTO; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; + +public class MultiRegattaListStepsBody extends UIObject implements RequiresResize { + + private static final StringMessages I18N = StringMessages.INSTANCE; + private static MultiRegattaListStepsBodyUiBinder uiBinder = GWT.create(MultiRegattaListStepsBodyUiBinder.class); + + interface MultiRegattaListStepsBodyUiBinder extends UiBinder { + } + + @UiField DivElement nameUi; + @UiField DivElement checkUi; + @UiField DivElement progressUi; + @UiField DivElement fleetsContainerUi; + + private final String seriesName; + + public MultiRegattaListStepsBody(RegattaProgressSeriesDTO seriesProgress) { + setElement(uiBinder.createAndBindUi(this)); + nameUi.getStyle().setVisibility(Visibility.HIDDEN); + seriesName = DEFAULT_SERIES_NAME.equals(seriesProgress.getName()) ? I18N.races() : seriesProgress.getName(); + nameUi.setInnerText(seriesName); + if (seriesProgress.isCompleted()) { + progressUi.setInnerText(String.valueOf(seriesProgress.getTotalRaceCount())); + } else { + checkUi.getStyle().setDisplay(Display.NONE); + progressUi.setInnerText(I18N.currentOfTotal( + seriesProgress.getProgressRaceCount(), seriesProgress.getTotalRaceCount())); + } + addFleetProgresses(seriesProgress.getFleetState(), seriesProgress.getTotalRaceCount()); + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + renderNames(); + } + }); + } + + private void addFleetProgresses(Map fleetStates, int totalRaceCount) { + double height = fleetStates.isEmpty() ? 100.0 : 100.0 / fleetStates.size(); + for (Entry fleetState : fleetStates.entrySet()) { + double finishedWidth = (fleetState.getValue().getFinishedRaceCount() * 100.0) / totalRaceCount; + double liveWidth = (fleetState.getValue().getFinishedAndLiveRaceCount() * 100.0) / totalRaceCount; + String fleetColor = fleetState.getKey().getFleetColor(); + MultiRegattaListStepsBodyFleet fleet = new MultiRegattaListStepsBodyFleet(finishedWidth, liveWidth, height, fleetColor); + fleetsContainerUi.appendChild(fleet.getElement()); + } + } + + private void renderNames() { + if (getElement().getOffsetWidth() < nameUi.getOffsetWidth() + checkUi.getOffsetWidth() + + progressUi.getOffsetWidth()) { + String[] tokens = seriesName.split(" "); + StringBuilder initials = new StringBuilder(); + for (int i = 0; i < tokens.length; i++) { + initials.append(tokens[i].charAt(0)); + } + nameUi.setInnerText(initials.toString()); + } + nameUi.setTitle(seriesName); + nameUi.getStyle().setVisibility(Visibility.VISIBLE); + } + + @Override + public void onResize() { + renderNames(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBody.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBody.ui.xml new file mode 100644 index 00000000000..a908b6e1ddf --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBody.ui.xml @@ -0,0 +1,15 @@ + + + + + +
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBodyFleet.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBodyFleet.java new file mode 100644 index 00000000000..94474e460a1 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBodyFleet.java @@ -0,0 +1,30 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.UIObject; + +public class MultiRegattaListStepsBodyFleet extends UIObject { + + private static MultiRegattaListStepsBodyFleetUiBinder uiBinder = GWT.create(MultiRegattaListStepsBodyFleetUiBinder.class); + + interface MultiRegattaListStepsBodyFleetUiBinder extends UiBinder { + } + + @UiField DivElement fleetProgressBarUi; + @UiField DivElement fleetProgressBarLiveUi; + + public MultiRegattaListStepsBodyFleet(double finishedWidth, double liveWidth, double height, String color) { + setElement(uiBinder.createAndBindUi(this)); + getElement().getStyle().setHeight(height, Unit.PCT); + fleetProgressBarUi.getStyle().setWidth(finishedWidth, Unit.PCT); + fleetProgressBarUi.getStyle().setBackgroundColor(color); + fleetProgressBarLiveUi.getStyle().setWidth(liveWidth, Unit.PCT); + fleetProgressBarLiveUi.getStyle().setBackgroundColor("#ff4040"); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBodyFleet.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBodyFleet.ui.xml new file mode 100644 index 00000000000..fe69964d172 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsBodyFleet.ui.xml @@ -0,0 +1,11 @@ + + + + + +
+
+
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsLegend.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsLegend.java new file mode 100644 index 00000000000..8fd14b1c925 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsLegend.java @@ -0,0 +1,19 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.user.client.ui.Widget; + +public class MultiRegattaListStepsLegend extends Widget { + + private static MultiRegattaListStepsLegendUiBinder uiBinder = GWT.create(MultiRegattaListStepsLegendUiBinder.class); + + interface MultiRegattaListStepsLegendUiBinder extends UiBinder { + } + + public MultiRegattaListStepsLegend() { + setElement(uiBinder.createAndBindUi(this)); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsLegend.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsLegend.ui.xml new file mode 100644 index 00000000000..420b9d0cd60 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/multiRegattaList/MultiRegattaListStepsLegend.ui.xml @@ -0,0 +1,45 @@ + + + + + .multiregatta_steps_legend { + font-size: 1rem; + text-align: right; + } + .multiregatta_steps_legend_item { + display: inline-block; + padding: 0.4em 0.666666666666667em; + margin: 0.333333333333333em 0 0.333333333333333em 0.666666666666667em; + font-size: 0.666em; + font-weight: 700; + line-height: 0.666666666666667em; + border: 1px solid #008fcc; + } + .multiregatta_steps_legend_item_finished { + color: #fff; + border-color: #ccc; + background-color: #f0ab00; + } + .multiregatta_steps_legend_item_live { + color: #fff; + border-color: #e94a1b; + background-color: #e94a1b; + } + .multiregatta_steps_legend_item_planned { + color: #ccc; + border-color: #ccc; + background-color: #fff; + } + +
+
+ +
+
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetition.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetition.gss new file mode 100644 index 00000000000..b1a13044c53 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetition.gss @@ -0,0 +1,174 @@ +/** + * Competitions View Component for regatta races + */ +.phase { + font-size: 1rem; + margin-bottom: 2em; +} +.phase_head { + padding: 1.333333333333333em 1.333333333333333em 2.666666666666667em 2em; + background-color: #f2f2f2; + border: 1px solid #dedede; + border-radius: 0.333333333333333em 0.333333333333333em 0 0; + font-weight: 600; + color: #666; +} +.phase_head_title { + text-transform: uppercase; + float: left; +} +.phase_head_info { + font-size: 0.866666666666667em; + text-align: right; + float: right; +} +.phase_head_info_item { + display: inline-block; + padding-left: 0.769230769230769em; + margin-left: 0.769230769230769em; + border-left: 1px solid #dedede; +} +.phase_head_info_item:first-child { + border: none; + padding-left: 0; + margin-left: 0; +} +.regattalistitem .phase { + margin: 0; +} +.regattalistitem .phase_head { + background-color: transparent; + border: none; + border-radius: 0; + border-bottom: 1px solid #dedede; +} +.fleet { + font-size: 1rem; + border: 1px solid #dedede; + border-top: none; + position: relative; +} +.fleet:last-child { + border-radius: 0 0 0.333333333333333em 0.333333333333333em; +} +.fleet_corner { + position: absolute; + content: ''; + display: block; + top: 1px; + left: 1px; + border-style: solid; + border-width: 1em 1em 0 0; + border-color: transparent; +} +.fleet_details { + color: #333; + position: absolute; + top: 1.333333333333333em; + left: 2em; + width: 20%; +} +@media (min-width: 61em) { + .fleet_details { + width: 13%; + } +} +.fleet_details_name { + font-weight: 600; +} +.fleet_details_competitors { + font-size: 0.866666666666667em; + color: #666; +} +.fleet_races { + padding: 0 1.333333333333333em 1% 25%; +} +@media (min-width: 61em) { + .fleet_races { + padding-left: 17%; + } +} +.fleet_races_race { + display: inline-block; + width: 19%; + margin: 1% 0 0 1%; + height: 4em; + line-height: 3.333333333333333em; + background-color: #008fcc; + border-radius: 0.2em; + -webkit-transition: background 0.2s; + -moz-transition: background 0.2s; + transition: background 0.2s; + text-decoration: none; + text-align: center; + color: #fff; + padding: 0.333333333333333em 0.333333333333333em; +} +@media (min-width: 45em) { + .fleet_races_race { + width: 15.66%; + line-height: inherit; + } +} +@media (min-width: 61em) { + .fleet_races_race { + width: 9%; + } +} +.fleet_races_race:hover { + background-color: #0081b8; + text-decoration: none; +} +.fleet_races_race_name, +.fleet_races_race_date { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.fleet_races_race_name { + font-size: 1.466666666666667em; + font-weight: 700; +} +.fleet_races_race_name:after { + content: ''; + display: inline-block; + width: 0.466666666666667em; + height: 0.466666666666667em; + background: url("../images/home/skip-forward.png") no-repeat center center; + margin: 0 0 0.133333333333333em 0.133333333333333em; +} +.fleet_races_race_date { + width: 100%; + font-size: 0.666666666666667em; + font-weight: 600; +} +.fleet_races_raceuntracked { + background-color: #999; +} +.fleet_races_raceuntracked:hover { + background-color: #8a8a8a; +} +.fleet_races_racelive { + background-color: #e94a1b; +} +.fleet_races_racelive:hover { + background-color: #d54115; +} +.fleet_races_raceplanned { + color: #999; + background-color: #fff; + border: 1px solid #999; +} +.fleet_races_raceplanned:hover { + background-color: #e6e6e6; +} +.fleet_races_raceplanned .fleet_races_race_name:after { + display: none; +} +.regattalistitem .fleet { + border: none; + border-bottom: 1px solid #dedede; +} +.regattalistitem .fleet:last-of-type { + border-radius: 0; +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleet.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleet.java new file mode 100644 index 00000000000..f33e9247121 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleet.java @@ -0,0 +1,38 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.raceCompetition; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Element; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListFleetDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListRaceDTO; + +public class RegattaCompetitionFleet extends Widget { + + private static RegattaCompetitionFleetUiBinder uiBinder = GWT.create(RegattaCompetitionFleetUiBinder.class); + + interface RegattaCompetitionFleetUiBinder extends UiBinder { + } + + @UiField DivElement fleetCornerUi; + @UiField DivElement fleetNameUi; + @UiField DivElement competitorCountUi; + @UiField DivElement racesContainerUi; + + public RegattaCompetitionFleet(RaceListFleetDTO fleet) { + setElement(uiBinder.createAndBindUi(this)); + this.competitorCountUi.setInnerText(fleet.getCompetitorCount() + " Comp. TODO"); + if (fleet.getFleet() != null) { + fleetNameUi.setInnerText(fleet.getFleet().getFleetName()); + fleetCornerUi.getStyle().setProperty("borderTopColor", fleet.getFleet().getFleetColor()); + } + } + + public void addRace(RaceListRaceDTO race, String raceViewerURL) { + RegattaCompetitionFleetRace competitionRace = new RegattaCompetitionFleetRace(race, raceViewerURL); + racesContainerUi.appendChild(competitionRace.getElement()); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleet.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleet.ui.xml new file mode 100644 index 00000000000..2b07b75d6be --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleet.ui.xml @@ -0,0 +1,14 @@ + + + + +
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleetRace.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleetRace.java new file mode 100644 index 00000000000..8bbe4ce4aad --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleetRace.java @@ -0,0 +1,48 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.raceCompetition; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.AnchorElement; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.UIObject; +import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListRaceDTO; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceTrackingState; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; + +public class RegattaCompetitionFleetRace extends UIObject { + + private static RegattaCompetitionFleetRaceUiBinder uiBinder = GWT.create(RegattaCompetitionFleetRaceUiBinder.class); + + interface RegattaCompetitionFleetRaceUiBinder extends UiBinder { + } + + @UiField RegattaCompetitionResources local_res; + @UiField DivElement raceNameUi; + @UiField DivElement raceDateUi; + private final AnchorElement anchor; + + public RegattaCompetitionFleetRace(RaceListRaceDTO race, String raceViewerUrl) { + anchor = uiBinder.createAndBindUi(this); + anchor.setHref(raceViewerUrl); + local_res.css().ensureInjected(); + setupRaceState(race.getTrackingState(), race.getViewState()); + this.raceNameUi.setInnerText(race.getRaceName()); + if (race.getStart() != null) { + this.raceDateUi.setInnerText(DateAndTimeFormatterUtil.defaultDateFormatter.render(race.getStart())); + } + setElement(anchor); + } + + private void setupRaceState(RaceTrackingState trackingState, RaceViewState viewState) { + if (viewState == RaceViewState.RUNNING) { + anchor.addClassName(local_res.css().fleet_races_racelive()); + } else if (viewState == RaceViewState.PLANNED || viewState == RaceViewState.SCHEDULED) { + anchor.addClassName(local_res.css().fleet_races_raceplanned()); + } + if (trackingState != RaceTrackingState.TRACKED_VALID_DATA) { + anchor.addClassName(local_res.css().fleet_races_raceuntracked()); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleetRace.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleetRace.ui.xml new file mode 100644 index 00000000000..081f37ebbec --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionFleetRace.ui.xml @@ -0,0 +1,10 @@ + + + + + +
+
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionResources.java new file mode 100644 index 00000000000..467fb3802e2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionResources.java @@ -0,0 +1,33 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.raceCompetition; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.CssResource; + +public interface RegattaCompetitionResources extends ClientBundle { + public static final RegattaCompetitionResources INSTANCE = GWT.create(RegattaCompetitionResources.class); + + @Source("RegattaCompetition.gss") + LocalCss css(); + + public interface LocalCss extends CssResource { + String phase(); + String phase_head(); + String phase_head_title(); + String phase_head_info(); + String phase_head_info_item(); + String regattalistitem(); + String fleet(); + String fleet_corner(); + String fleet_details(); + String fleet_details_name(); + String fleet_details_competitors(); + String fleet_races(); + String fleet_races_race(); + String fleet_races_race_name(); + String fleet_races_race_date(); + String fleet_races_raceuntracked(); + String fleet_races_racelive(); + String fleet_races_raceplanned(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionSeries.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionSeries.java new file mode 100644 index 00000000000..ae417f68822 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionSeries.java @@ -0,0 +1,47 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.raceCompetition; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.sap.sailing.gwt.home.client.place.event.regatta.EventRegattaView.Presenter; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListFleetDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListRaceDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListSeriesDTO; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceTrackingState; + +public class RegattaCompetitionSeries extends Composite { + + private static RegattaCompetitionSeriesUiBinder uiBinder = GWT.create(RegattaCompetitionSeriesUiBinder.class); + + interface RegattaCompetitionSeriesUiBinder extends UiBinder { + } + + @UiField DivElement seriesNameUi; + @UiField DivElement competitorCountUi; + @UiField DivElement raceCountUi; + private final HTMLPanel containerUi; + + public RegattaCompetitionSeries(Presenter presenter, RaceListSeriesDTO series) { + initWidget(containerUi = uiBinder.createAndBindUi(this)); + this.seriesNameUi.setInnerText(series.getSeriesName()); + this.competitorCountUi.setInnerText(series.getCompetitorCount() + " Comp. TODO"); + this.raceCountUi.setInnerText(series.getRaceCount() + " Races TODO"); + for (RaceListFleetDTO fleet : series.getFleets()) { + addFleet(presenter, fleet); + } + } + + public void addFleet(Presenter presenter, RaceListFleetDTO fleet) { + RegattaCompetitionFleet competitionFleet = new RegattaCompetitionFleet(fleet); + for (RaceListRaceDTO race : fleet.getRaces()) { + boolean tracked = race.getTrackingState() == RaceTrackingState.TRACKED_VALID_DATA; + String raceViewerUrl = tracked ? presenter.getRaceViewerURL(race.getRegattaName(), race.getTrackedRaceName()) : null; + competitionFleet.addRace(race, raceViewerUrl); + } + containerUi.add(competitionFleet); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionSeries.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionSeries.ui.xml new file mode 100644 index 00000000000..dcc326803c6 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceCompetition/RegattaCompetitionSeries.ui.xml @@ -0,0 +1,15 @@ + + + + + +
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.gss index 08b7cd10688..085c79ffdc4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.gss @@ -66,7 +66,7 @@ } .race_itemwinner { overflow: hidden; - white-space: nowrap; + white-space: normal; text-overflow: ellipsis; vertical-align: middle; min-width: 13.333333333333334em; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.java index 02f3da97a1f..43c16bf8745 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.java @@ -1,40 +1,77 @@ package com.sap.sailing.gwt.home.client.place.event.partials.raceListLive; -import com.google.gwt.core.client.GWT; +import java.util.Collection; + import com.google.gwt.dom.client.Style.Display; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.home.client.place.event.EventView; -import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceList; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.AbstractRaceList; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListColumnFactory; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListContainer; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListDataUtil; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.SortableRaceListColumn; import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshableWidget; -import com.sap.sailing.gwt.ui.shared.dispatch.event.LiveRacesDTO; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.dispatch.CollectionResult; +import com.sap.sailing.gwt.ui.shared.dispatch.event.LiveRaceDTO; -public class RacesListLive extends Composite implements RefreshableWidget { +public class RacesListLive extends Composite { - private static RacesListLiveUiBinder uiBinder = GWT.create(RacesListLiveUiBinder.class); + private final RaceListLiveRaces raceList; + private final RaceListContainer raceListContainer; - interface RacesListLiveUiBinder extends UiBinder { - } - - @UiField(provided = true) RaceList raceList; - public RacesListLive(EventView.Presenter presenter, boolean showRegattaDetails) { - RacesListLiveResources.INSTANCE.css().ensureInjected(); - raceList = new RaceList(presenter, showRegattaDetails); - initWidget(uiBinder.createAndBindUi(this)); + raceList = new RaceListLiveRaces(presenter, showRegattaDetails); + raceListContainer = new RaceListContainer(StringMessages.INSTANCE.liveNow(), raceList); + initWidget(raceListContainer); getElement().getStyle().setDisplay(Display.NONE); } - @Override - public void setData(LiveRacesDTO data, long nextUpdate, int updateNo) { - if(data == null || data.getRaces().isEmpty()) { - getElement().getStyle().setDisplay(Display.NONE); - } else { - getElement().getStyle().clearDisplay(); - this.raceList.setListData(data); + public RefreshableWidget> getRefreshable() { + return raceListContainer; + } + + private class RaceListLiveRaces extends AbstractRaceList { + private final SortableRaceListColumn regattaNameColumn = RaceListColumnFactory.getRegattaNameColumn(); + private final SortableRaceListColumn flagsColumn = RaceListColumnFactory.getFlagsColumn(); + private final SortableRaceListColumn windSpeedColumn = RaceListColumnFactory.getWindSpeedColumn(); + private final SortableRaceListColumn courseAreaColumn = RaceListColumnFactory.getCourseAreaColumn(); + private final SortableRaceListColumn courseColumn = RaceListColumnFactory.getCourseColumn(); + private final SortableRaceListColumn raceViewStateColumn = RaceListColumnFactory.getRaceViewStateColumn(); + + public RaceListLiveRaces(EventView.Presenter presenter, boolean showRegattaDetails) { + super(presenter); + this.regattaNameColumn.setShowDetails(showRegattaDetails); + } + + @Override + protected void setTableData(Collection data) { + boolean hasFleets = RaceListDataUtil.hasFleets(data); + this.fleetCornerColumn.setShowDetails(hasFleets); + this.fleetNameColumn.setShowDetails(hasFleets); + this.startTimeColumn.setShowTimeOnly(!RaceListDataUtil.hasDifferentStartDates(data)); + this.courseAreaColumn.setShowDetails(RaceListDataUtil.hasCourseAreas(data)); + this.courseColumn.setShowDetails(RaceListDataUtil.hasCourses(data)); + boolean hasWind = RaceListDataUtil.hasWind(data); + this.windSpeedColumn.setShowDetails(hasWind); + this.windDirectionColumn.setShowDetails(hasWind); + super.setTableData(data); + } + + @Override + protected void initTableColumns() { + add(fleetCornerColumn); + add(regattaNameColumn); + add(raceNameColumn); + add(fleetNameColumn); + add(startTimeColumn); + add(flagsColumn); + add(windSpeedColumn); + add(windDirectionColumn); + add(courseAreaColumn); + add(courseColumn); + add(raceViewStateColumn); + add(raceViewerButtonColumn); } } - } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.ui.xml deleted file mode 100644 index cf0b0b5e149..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/raceListLive/RacesListLive.ui.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - -

- -

- -
-
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java index e840375c101..623dc8c5dca 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java @@ -1,243 +1,64 @@ package com.sap.sailing.gwt.home.client.place.event.partials.racelist; import java.util.Collection; -import java.util.Date; -import com.google.gwt.cell.client.AbstractCell; -import com.google.gwt.cell.client.Cell; -import com.google.gwt.cell.client.DateCell; -import com.google.gwt.cell.client.TextCell; -import com.google.gwt.core.client.GWT; -import com.google.gwt.dom.client.Style.TextTransform; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; -import com.google.gwt.safecss.shared.SafeStyles; -import com.google.gwt.safecss.shared.SafeStylesBuilder; -import com.google.gwt.safecss.shared.SafeStylesUtils; -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; +import com.google.gwt.user.cellview.client.ColumnSortList; +import com.google.gwt.user.cellview.client.ColumnSortList.ColumnSortInfo; import com.google.gwt.user.cellview.client.Header; import com.google.gwt.user.cellview.client.RowStyles; -import com.google.gwt.user.cellview.client.TextHeader; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.SimplePanel; import com.sap.sailing.domain.common.InvertibleComparator; -import com.sap.sailing.domain.common.SortingOrder; -import com.sap.sailing.domain.common.impl.NaturalComparator; -import com.sap.sailing.gwt.common.client.SharedResources; -import com.sap.sailing.gwt.common.client.SharedResources.MainCss; -import com.sap.sailing.gwt.common.client.SharedResources.MediaCss; -import com.sap.sailing.gwt.common.client.i18n.TextMessages; import com.sap.sailing.gwt.home.client.place.event.EventView; import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources; import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources.LocalCss; -import com.sap.sailing.gwt.regattaoverview.client.FlagsMeaningExplanator; -import com.sap.sailing.gwt.regattaoverview.client.SailingFlagsBuilder; -import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.client.shared.controls.SortableColumn; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListColumnFactory.SortableRaceListStartTimeColumn; import com.sap.sailing.gwt.ui.leaderboard.SortedCellTable; -import com.sap.sailing.gwt.ui.shared.dispatch.event.LiveRaceDTO; -import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; -import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; -import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceTrackingState; -import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; -import com.sap.sailing.gwt.ui.shared.race.RaceProgressDTO; -import com.sap.sailing.gwt.ui.shared.race.SimpleWindDTO; -import com.sap.sailing.gwt.ui.shared.util.NullSafeComparableComparator; -import com.sap.sse.common.Util; -import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sailing.gwt.ui.shared.race.wind.AbstractWindDTO; import com.sap.sse.gwt.theme.client.component.celltable.CleanCellTableResources; import com.sap.sse.gwt.theme.client.component.celltable.StyledHeaderOrFooterBuilder; -public abstract class AbstractRaceList extends Composite { +public abstract class AbstractRaceList> extends Composite { private static final LocalCss CSS = RacesListLiveResources.INSTANCE.css(); - private static final MediaCss MEDIA_CSS = SharedResources.INSTANCE.mediaCss(); - private static final StringMessages I18N = StringMessages.INSTANCE; - private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class); - - interface CellTemplates extends SafeHtmlTemplates { - @Template("
") - SafeHtml fleetCorner(String styleNames, SafeStyles color); - - @Template("
{3}
") - SafeHtml viewStateRunning(String styleNamesBar, String styleNamesProgress, SafeStyles width, String text); - - @Template("") - SafeHtml windDirection(SafeStyles rotation); - - @Template("
{1}
") - SafeHtml raceNotTracked(SafeStyles styles, String text); - - @Template("{1}") - SafeHtml raceViewerLinkButton(String styleNames, String text, String link); - } private final SimplePanel cellTableContainer = new SimplePanel(); - private final EventView.Presenter presenter; - protected final SortableRaceListColumn fleetCornerColumn = new RaceListColumn("", - new FleetCornerCell()) { - @Override - public String getColumnStyle() { - return CSS.race_fleetcorner(); - } - - @Override - public FleetMetadataDTO getValue(T object) { - return object.getFleet(); - } - }; - protected final SortableRaceListColumn raceNameColumn = new SortableRaceListColumn(I18N.race(), - new TextCell(), new InvertibleComparatorWrapper(new NaturalComparator(false)) { - @Override - protected String getComparisonValue(T object) { - return object.getRaceName(); - } - }) { - @Override - public String getHeaderStyle() { - return CSS.raceslist_head_item(); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), CSS.race_itemname()); - } - - @Override - public String getValue(T object) { - return object.getRaceName(); - } - }; - protected final SortableRaceListColumn fleetNameColumn = new SortableRaceListColumn(I18N.fleet(), - new TextCell(), new InvertibleComparatorWrapper(new NaturalComparator(false)) { - @Override - protected String getComparisonValue(T object) { - return object.getFleet() != null ? object.getFleet().getFleetName() : null; - } - }) { - @Override - public String getHeaderStyle() { - return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); - } - - @Override - public String getValue(T object) { - return object.getFleet() != null ? object.getFleet().getFleetName() : null; - } - }; - protected final SortableRaceListColumn startTimeColumn = new SortableRaceListColumn(I18N.start(), - new DateCell(DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE)), - new InvertibleComparatorWrapper(new NullSafeComparableComparator()) { - @Override - protected Date getComparisonValue(T object) { - return object.getStart(); - } - }) { - @Override - public String getHeaderStyle() { - return CSS.raceslist_head_item(); - } - - @Override - public String getColumnStyle() { - return CSS.race_item(); - } - - @Override - public Date getValue(T object) { - return object.getStart(); - } - }; - protected final SortableRaceListColumn courseAreaColumn = new SortableRaceListColumn(I18N.courseArea(), - new TextCell(), new InvertibleComparatorWrapper(new NaturalComparator(false)) { - @Override - protected String getComparisonValue(T object) { - return object.getCourseArea(); - } - }) { - @Override - public String getHeaderStyle() { - return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); - } - - @Override - public String getValue(T object) { - return object.getCourseArea(); - } - }; - protected final SortableRaceListColumn courseColumn = new SortableRaceListColumn(I18N.course(), - new TextCell(), new InvertibleComparatorWrapper(new NaturalComparator(false)) { - @Override - protected String getComparisonValue(T object) { - return object.getCourse(); - } - }) { - @Override - public String getHeaderStyle() { - return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); - } - - @Override - public String getValue(T object) { - return object.getCourse(); - } - }; - protected final SortableRaceListColumn raceViewerButtonColumn = new RaceListColumn("", - new RaceViewerButtonCell()) { - @Override - public String getHeaderStyle() { - return getStyleNamesString(CSS.raceslist_head_item(), CSS.raceslist_head_itembutton()); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), CSS.race_itemright()); - } - - @Override - public T getValue(T object) { - return object; - } - }; + protected final SortableRaceListColumn fleetCornerColumn = RaceListColumnFactory.getFleetCornerColumn(); + protected final SortableRaceListColumn raceNameColumn = RaceListColumnFactory.getRaceNameColumn(); + protected final SortableRaceListColumn fleetNameColumn = RaceListColumnFactory.getFleetNameColumn(); + protected final SortableRaceListStartTimeColumn startTimeColumn = RaceListColumnFactory.getStartTimeColumn(); + protected final SortableRaceListColumn windDirectionColumn = RaceListColumnFactory.getWindDirectionColumn(); + protected final SortableRaceListColumn raceViewerButtonColumn; private SortedCellTable cellTable; protected AbstractRaceList(EventView.Presenter presenter) { CSS.ensureInjected(); - this.presenter = presenter; + this.raceViewerButtonColumn = RaceListColumnFactory.getRaceViewerButtonColumn(presenter); this.initWidget(cellTableContainer); } + @SuppressWarnings("unchecked") protected void setTableData(Collection data) { - Column sortColumn = (this.cellTable == null ? null : this.cellTable.getCurrentlySortedColumn()); + ColumnSortList sortList = this.cellTable == null ? null : this.cellTable.getColumnSortList(); this.cellTable = new SortedCellTable(data.size(), CleanCellTableResources.INSTANCE); this.cellTableContainer.setWidget(this.cellTable); this.initTableStyle(); this.initTableColumns(); this.cellTable.setList(data); - if (sortColumn != null && this.cellTable.getColumnIndex(sortColumn) >= 0) { - this.cellTable.sortColumn(sortColumn); + if (sortList != null && sortList.size() > 0) { + for (int i = sortList.size() - 1; i >= 0; i--) { + ColumnSortInfo sortInfo = sortList.get(i); + Column column = (Column) sortInfo.getColumn(); + if(this.cellTable.getColumnIndex(column) >= 0) { + this.cellTable.sortColumn(column, sortInfo.isAscending()); + } + } + } else { + this.cellTable.sortColumn(startTimeColumn); } } @@ -254,124 +75,22 @@ public abstract class AbstractRaceList extends Compos protected abstract void initTableColumns(); - protected void add(SortableRaceListColumn column) { - if (column.getColumnStyle() != null) { - column.setCellStyleNames(column.getColumnStyle()); - } - Header header = column.getHeader(); - if (header != null && column.getHeaderStyle() != null) { - header.setHeaderStyleNames(column.getHeaderStyle()); - } - boolean ascending = column.getPreferredSortingOrder().isAscending(); - this.cellTable.addColumn(column, header, column.getComparator(), ascending); - } - - protected abstract class SortableRaceListColumn extends SortableColumn { - private final String headerText; - private final InvertibleComparator comparator; - - protected SortableRaceListColumn(String headerText, Cell cell, InvertibleComparator comparator) { - super(cell, SortingOrder.ASCENDING); - this.headerText = headerText; - this.comparator = comparator; - } - - @Override - public final InvertibleComparator getComparator() { - return comparator; - } - - @Override - public final Header getHeader() { - return new TextHeader(headerText); - } - - protected final String getStyleNamesString(String... styleNames) { - return Util.join(" ", styleNames); - } - } - - protected abstract class RaceListColumn extends SortableRaceListColumn { - protected RaceListColumn(String headerText, Cell cell) { - super(headerText, cell, null); - } - } - - private class FleetCornerCell extends AbstractCell { - @Override - public void render(Context context, FleetMetadataDTO value, SafeHtmlBuilder sb) { - if (value != null) { - sb.append(TEMPLATE.fleetCorner(CSS.race_fleetcorner_icon(), - SafeStylesUtils.fromTrustedNameAndValue("border-top-color", value.getFleetColor()))); + protected void add(SortableRaceListColumn column) { + if (column.isShowDetails()) { + if (column.getColumnStyle() != null) { + column.setCellStyleNames(column.getColumnStyle()); } - } - } - - protected class FlagsCell extends AbstractCell { - @Override - public void render(Context context, FlagStateDTO value, SafeHtmlBuilder sb) { - if (value != null) { - sb.append(SailingFlagsBuilder.render(value, 0.55, FlagsMeaningExplanator.getFlagsMeaning(I18N, - value.getLastUpperFlag(), value.getLastLowerFlag(), value.isLastFlagsAreDisplayed()))); + Header header = column.getHeader(); + if (header != null && column.getHeaderStyle() != null) { + header.setHeaderStyleNames(column.getHeaderStyle()); } - } - } - - protected class WindDirectionCell extends AbstractCell { - @Override - public void render(Context context, SimpleWindDTO value, SafeHtmlBuilder sb) { - if (value != null) { - SafeStylesBuilder safeStyles = new SafeStylesBuilder(); - safeStyles.trustedNameAndValue("-webkit-transform", "rotate(" + value.getTrueWindFromDeg() + "deg)"); - safeStyles.trustedNameAndValue("transform", "rotate(" + value.getTrueWindFromDeg() + "deg)"); - safeStyles.width(2.75, Unit.EM).height(2.75, Unit.EM); - sb.append(TEMPLATE.windDirection(safeStyles.toSafeStyles())); + boolean ascending = column.getPreferredSortingOrder().isAscending(); + InvertibleComparator comperator = column.getComparator(); + if (comperator != null) { + comperator.setAscending(ascending); } + this.cellTable.addColumn(column, header, comperator, ascending); } } - - protected class RaceViewStateCell extends AbstractCell { - @Override - public void render(Context context, LiveRaceDTO value, SafeHtmlBuilder sb) { - RaceViewState state = value.getViewState(); - RaceProgressDTO progress = value.getProgress(); - if (state == RaceViewState.SCHEDULED) { - double min = MillisecondsTimePoint.now().until(new MillisecondsTimePoint(value.getStart())).asMinutes(); - sb.appendEscaped(I18N.startingInMinutes((int) min)); - } else if (state == RaceViewState.RUNNING && progress != null) { - SafeStyles width = SafeStylesUtils.forWidth(progress.getPercentageProgress(), Unit.PCT); - String text = I18N.currentOfTotalLegs(progress.getCurrentLeg(), progress.getTotalLegs()); - sb.append(TEMPLATE.viewStateRunning(CSS.race_itemstatus_progressbar(), - CSS.race_itemstatus_progressbar_progress(), width, text)); - } else { - sb.appendEscaped(state.getLabel()); - } - } - } - - private class RaceViewerButtonCell extends AbstractCell { - private final TextMessages I18N_UBI = TextMessages.INSTANCE; - private final MainCss css = SharedResources.INSTANCE.mainCss(); - private final String watchNowStyle = getButtonStyleNames(css.buttonred()); - private final String analyseRaceStyle = getButtonStyleNames(css.buttonprimary()); - - @Override - public void render(Context context, T data, SafeHtmlBuilder sb) { - if (data.getTrackingState() != RaceTrackingState.TRACKED_VALID_DATA) { - SafeStylesBuilder styles = new SafeStylesBuilder().textTransform(TextTransform.UPPERCASE); - styles.trustedColor("#666").trustedNameAndValue("font-size", "0.933333333333333rem").toSafeStyles(); - styles.trustedNameAndValue("padding", "0.714285714285714em 1.071428571428571em"); - sb.append(TEMPLATE.raceNotTracked(styles.toSafeStyles(), I18N_UBI.eventRegattaRaceNotTracked())); - } else { - String styleNames = data.getViewState() == RaceViewState.FINISHED ? analyseRaceStyle : watchNowStyle; - String text = data.getViewState() == RaceViewState.FINISHED ? I18N.analyseRace() : I18N_UBI.watchNow(); - String raceViewerURL = presenter.getRaceViewerURL(data.getRegattaName(), data.getTrackedRaceName()); - sb.append(TEMPLATE.raceViewerLinkButton(styleNames, text, raceViewerURL)); - } - } - - private final String getButtonStyleNames(String buttonColor) { - return Util.join(" ", css.button(), css.buttonstrong(), buttonColor, css.buttonarrowrightwhite()); - } - } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceList.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceList.java deleted file mode 100644 index b4125a82ed6..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceList.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.sap.sailing.gwt.home.client.place.event.partials.racelist; - -import com.google.gwt.cell.client.TextCell; -import com.sap.sailing.domain.common.impl.NaturalComparator; -import com.sap.sailing.gwt.common.client.SharedResources; -import com.sap.sailing.gwt.common.client.SharedResources.MediaCss; -import com.sap.sailing.gwt.home.client.place.event.EventView; -import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources; -import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources.LocalCss; -import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.shared.dispatch.event.LiveRaceDTO; -import com.sap.sailing.gwt.ui.shared.dispatch.event.LiveRacesDTO; -import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; -import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; -import com.sap.sailing.gwt.ui.shared.race.SimpleWindDTO; -import com.sap.sailing.gwt.ui.shared.util.NullSafeComparableComparator; - -public class RaceList extends AbstractRaceList { - - private static final LocalCss CSS = RacesListLiveResources.INSTANCE.css(); - private static final MediaCss MEDIA_CSS = SharedResources.INSTANCE.mediaCss(); - private static final StringMessages I18N = StringMessages.INSTANCE; - - private final boolean showRegattaDetails; - private boolean showFleetDetails; - private boolean showCourseAreaDetails; - private boolean showCourseDetails; - private boolean showWindDetails; - - private final SortableRaceListColumn regattaNameColumn = new SortableRaceListColumn(I18N.regatta(), - new TextCell(), new InvertibleComparatorWrapper(new NaturalComparator(false)) { - @Override - protected String getComparisonValue(LiveRaceDTO object) { - return object.getRegattaDisplayName(); - } - }) { - @Override - public String getHeaderStyle() { - return CSS.raceslist_head_item(); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), CSS.race_itemname()); - } - - @Override - public String getValue(LiveRaceDTO object) { - return object.getRegattaDisplayName(); - } - }; - private final SortableRaceListColumn flagsColumn = new RaceListColumn(I18N.flags(), - new FlagsCell()) { - @Override - public String getHeaderStyle() { - return CSS.raceslist_head_item(); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), CSS.racesListIcon()); - } - - @Override - public FlagStateDTO getValue(LiveRaceDTO object) { - return object.getFlagState(); - } - }; - private final SortableRaceListColumn windSpeedColumn = new SortableRaceListColumn(I18N.wind(), - new TextCell(), new InvertibleComparatorWrapper( - new NullSafeComparableComparator()) { - @Override - protected Double getComparisonValue(LiveRaceDTO object) { - return object.getWind() != null ? object.getWind().getTrueWindSpeedInKnots() : null; - } - }) { - @Override - public String getHeaderStyle() { - return CSS.raceslist_head_item(); - } - - @Override - public String getColumnStyle() { - return CSS.race_item(); - } - - @Override - public String getValue(LiveRaceDTO object) { - return object.getWind() != null ? I18N.knotsValue(object.getWind().getTrueWindSpeedInKnots()) : null; - } - }; - private final SortableRaceListColumn windDirectionColumn = new SortableRaceListColumn( - I18N.from(), new WindDirectionCell(), new InvertibleComparatorWrapper( - new NullSafeComparableComparator()) { - @Override - protected Double getComparisonValue(LiveRaceDTO object) { - return object.getWind() != null ? object.getWind().getTrueWindFromDeg() : null; - } - }) { - @Override - public String getHeaderStyle() { - return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.hideonsmall()); - } - - @Override - public String getColumnStyle() { - return getStyleNamesString(CSS.race_item(), CSS.racesListIcon(), MEDIA_CSS.hideonsmall()); - } - - @Override - public SimpleWindDTO getValue(LiveRaceDTO object) { - return object.getWind(); - } - }; - private final SortableRaceListColumn raceViewStateColumn = new SortableRaceListColumn( - I18N.status(), new RaceViewStateCell(), new InvertibleComparatorWrapper( - new NullSafeComparableComparator()) { - @Override - protected RaceViewState getComparisonValue(LiveRaceDTO object) { - return object.getViewState(); - } - }) { - @Override - public String getHeaderStyle() { - return CSS.raceslist_head_item(); - } - - @Override - public String getColumnStyle() { - return CSS.race_item(); - } - - @Override - public LiveRaceDTO getValue(LiveRaceDTO object) { - return object; - } - }; - - public RaceList(EventView.Presenter presenter, boolean showRegattaDetails) { - super(presenter); - this.showRegattaDetails = showRegattaDetails; - } - - public void setListData(LiveRacesDTO data) { - this.showFleetDetails = data.hasFleets(); - this.showCourseAreaDetails = data.hasCourseAreas(); - this.showCourseDetails = data.hasCourses(); - this.showWindDetails = data.hasWind(); - setTableData(data.getRaces()); - } - - @Override - protected void initTableColumns() { - if (showFleetDetails) { - add(fleetCornerColumn); - } - if (showRegattaDetails) { - add(regattaNameColumn); - } - add(raceNameColumn); - if (showFleetDetails) { - add(fleetNameColumn); - } - add(startTimeColumn); - add(flagsColumn); - if(showWindDetails) { - add(windSpeedColumn); - add(windDirectionColumn); - } - if (showCourseAreaDetails) { - add(courseAreaColumn); - } - if(showCourseDetails) { - add(courseColumn); - } - add(raceViewStateColumn); - add(raceViewerButtonColumn); - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListColumnFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListColumnFactory.java new file mode 100644 index 00000000000..c9f620d4c90 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListColumnFactory.java @@ -0,0 +1,625 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.racelist; + +import java.util.Date; + +import com.google.gwt.cell.client.AbstractCell; +import com.google.gwt.cell.client.Cell; +import com.google.gwt.cell.client.TextCell; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Style.TextTransform; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; +import com.google.gwt.safecss.shared.SafeStyles; +import com.google.gwt.safecss.shared.SafeStylesBuilder; +import com.google.gwt.safecss.shared.SafeStylesUtils; +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.safehtml.shared.SafeUri; +import com.google.gwt.safehtml.shared.UriUtils; +import com.google.gwt.user.cellview.client.SafeHtmlHeader; +import com.sap.sailing.domain.common.InvertibleComparator; +import com.sap.sailing.domain.common.SortingOrder; +import com.sap.sailing.domain.common.impl.NaturalComparator; +import com.sap.sailing.gwt.common.client.SharedResources; +import com.sap.sailing.gwt.common.client.SharedResources.MainCss; +import com.sap.sailing.gwt.common.client.SharedResources.MediaCss; +import com.sap.sailing.gwt.common.client.i18n.TextMessages; +import com.sap.sailing.gwt.home.client.place.event.EventView; +import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources; +import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources.LocalCss; +import com.sap.sailing.gwt.regattaoverview.client.FlagsMeaningExplanator; +import com.sap.sailing.gwt.regattaoverview.client.SailingFlagsBuilder; +import com.sap.sailing.gwt.ui.client.FlagImageResolver; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; +import com.sap.sailing.gwt.ui.shared.dispatch.event.LiveRaceDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListRaceDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.SimpleCompetitorDTO; +import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceTrackingState; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; +import com.sap.sailing.gwt.ui.shared.race.RaceProgressDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.AbstractWindDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.WindStatisticsDTO; +import com.sap.sailing.gwt.ui.shared.util.NullSafeComparableComparator; +import com.sap.sse.common.Duration; +import com.sap.sse.common.Util; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +public class RaceListColumnFactory { + + private static final LocalCss CSS = RacesListLiveResources.INSTANCE.css(); + private static final MainCss MAIN_CSS = SharedResources.INSTANCE.mainCss(); + private static final MediaCss MEDIA_CSS = SharedResources.INSTANCE.mediaCss(); + private static final StringMessages I18N = StringMessages.INSTANCE; + private static final TextMessages I18N_UBI = TextMessages.INSTANCE; + private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class); + + interface CellTemplates extends SafeHtmlTemplates { + @Template("
") + SafeHtml fleetCorner(String styleNames, SafeStyles color); + + @Template("
{3}
") + SafeHtml viewStateRunning(String styleNamesBar, String styleNamesProgress, SafeStyles width, String text); + + @Template("") + SafeHtml windDirection(SafeStyles rotation); + + @Template("
{1}
") + SafeHtml raceNotTracked(SafeStyles styles, String text); + + @Template("{1}") + SafeHtml raceViewerLinkButton(String styleNames, String text, String link); + + @Template("{3}") + SafeHtml winner(String styleNamesFlag, String styleNamesText, SafeUri flagImageURL, String name); + + @Template("") + SafeHtml imageHeader(String styleNames, SafeUri imageURL); + } + + public static > SortableRaceListColumn getFleetCornerColumn() { + Cell cell = new AbstractCell() { + @Override + public void render(Context context, FleetMetadataDTO value, SafeHtmlBuilder sb) { + if (value != null) { + SafeStyles style = SafeStylesUtils.fromTrustedNameAndValue("border-top-color", value.getFleetColor()); + sb.append(TEMPLATE.fleetCorner(CSS.race_fleetcorner_icon(), style)); + } + } + }; + InvertibleComparator comparator = null; + return new SortableRaceListColumn("", cell, comparator) { + @Override + public String getColumnStyle() { + return CSS.race_fleetcorner(); + } + + @Override + public FleetMetadataDTO getValue(T object) { + return object.getFleet(); + } + }; + } + + public static > SortableRaceListColumn getRegattaNameColumn() { + Cell cell = new TextCell(); + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NaturalComparator(false)) { + @Override + protected String getComparisonValue(T object) { + return object.getRegattaDisplayName(); + } + }; + return new SortableRaceListColumn(I18N.regatta(), cell, comparator) { + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.race_itemname()); + } + + @Override + public String getValue(T object) { + return object.getRegattaDisplayName(); + } + }; + } + + public static > SortableRaceListColumn getRaceNameColumn() { + Cell cell = new TextCell(); + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NaturalComparator(false)) { + @Override + protected String getComparisonValue(T object) { + return object.getRaceName(); + } + }; + return new SortableRaceListColumn(I18N.race(), cell, comparator) { + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.race_itemname()); + } + + @Override + public String getValue(T object) { + return object.getRaceName(); + } + }; + } + + public static > SortableRaceListColumn getFleetNameColumn() { + Cell cell = new TextCell(); + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NaturalComparator(false)) { + @Override + protected String getComparisonValue(T object) { + return object.getFleet() != null ? object.getFleet().getFleetName() : null; + } + }; + return new SortableRaceListColumn(I18N.fleet(), cell, comparator) { + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getValue(T object) { + return object.getFleet() != null ? object.getFleet().getFleetName() : null; + } + }; + } + + public static > SortableRaceListStartTimeColumn getStartTimeColumn() { + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator()) { + @Override + protected Date getComparisonValue(T object) { + return object.getStart(); + } + }; + return new SortableRaceListStartTimeColumn(comparator); + } + + public static SortableRaceListColumn getDurationColumn() { + Cell cell = new TextCell(); + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator(false)) { + @Override + protected Duration getComparisonValue(T object) { + return object.getDuration(); + } + }; + return new SortableRaceListColumn(I18N.durationPlain(), cell, comparator) { + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getValue(T object) { + return object.getDuration() != null ? DateAndTimeFormatterUtil.formatElapsedTime(object.getDuration().asMillis()) : null; + } + }; + } + + public static SortableRaceListColumn getFlagsColumn() { + Cell cell = new AbstractCell() { + @Override + public void render(Context context, FlagStateDTO value, SafeHtmlBuilder sb) { + if (value != null) { + sb.append(SailingFlagsBuilder.render(value, 0.55, FlagsMeaningExplanator.getFlagsMeaning(I18N, + value.getLastUpperFlag(), value.getLastLowerFlag(), value.isLastFlagsAreDisplayed()))); + } + } + }; + InvertibleComparator comparator = null; + return new SortableRaceListColumn(I18N.flags(), cell, comparator) { + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.racesListIcon()); + } + + @Override + public FlagStateDTO getValue(T object) { + return object.getFlagState(); + } + }; + } + + public static SortableRaceListColumn getWindSpeedColumn() { + InvertibleComparator comparator = new InvertibleComparatorWrapper( new NullSafeComparableComparator()) { + @Override + protected Double getComparisonValue(T object) { + return object.getWind() != null ? object.getWind().getTrueWindSpeedInKnots() : null; + } + }; + return new WindSpeedOrRangeColumn(comparator) { + @Override + public String getValue(T object) { + return object.getWind() != null ? I18N.knotsValue(object.getWind().getTrueWindSpeedInKnots()) : null; + } + }; + } + + public static SortableRaceListColumn getWindRangeColumn() { + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator()) { + @Override + protected Double getComparisonValue(T object) { + WindStatisticsDTO wind = object.getWind(); + return wind != null ? (wind.getTrueUpperboundWindInKnots() + wind .getTrueLowerboundWindInKnots()) : null; + } + }; + return new WindSpeedOrRangeColumn(comparator) { + @Override + public String getValue(T object) { + WindStatisticsDTO wind = object.getWind(); + if (wind == null) { + return ""; + } else if (wind.getRoundedDifference() <= 0d) { + return I18N.knotsValue(wind.getTrueLowerboundWindInKnots()); + } + return I18N.knotsRange(wind.getTrueLowerboundWindInKnots(), wind.getTrueUpperboundWindInKnots()); + } + }; + } + + public static > SortableRaceListColumn getWindDirectionColumn() { + Cell cell = new AbstractCell() { + @Override + public void render(Context context, AbstractWindDTO value, SafeHtmlBuilder sb) { + if (value != null) { + SafeStylesBuilder safeStyles = new SafeStylesBuilder(); + safeStyles.trustedNameAndValue("-webkit-transform", "rotate(" + value.getTrueWindFromDeg() + "deg)"); + safeStyles.trustedNameAndValue("transform", "rotate(" + value.getTrueWindFromDeg() + "deg)"); + safeStyles.width(2.75, Unit.EM).height(2.75, Unit.EM); + sb.append(TEMPLATE.windDirection(safeStyles.toSafeStyles())); + } + } + }; + InvertibleComparator comparator = new InvertibleComparatorWrapper( new NullSafeComparableComparator()) { + @Override + protected Double getComparisonValue(T object) { + return object.getWind() != null ? object.getWind().getTrueWindFromDeg() : null; + } + }; + return new SortableRaceListColumn(I18N.from(), cell, comparator) { + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.hideonsmall()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.racesListIcon(), MEDIA_CSS.hideonsmall()); + } + + @Override + public AbstractWindDTO getValue(T object) { + return object.getWind(); + } + }; + } + + public static > SortableRaceListColumn getCourseAreaColumn() { + Cell cell = new TextCell(); + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NaturalComparator(false)) { + @Override + protected String getComparisonValue(T object) { + return object.getCourseArea(); + } + }; + return new SortableRaceListColumn(I18N.courseArea(), cell, comparator) { + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getValue(T object) { + return object.getCourseArea(); + } + }; + } + + public static > SortableRaceListColumn getCourseColumn() { + Cell cell = new TextCell(); + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NaturalComparator(false)) { + @Override + protected String getComparisonValue(T object) { + return object.getCourse(); + } + }; + return new SortableRaceListColumn(I18N.course(), cell, comparator) { + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getValue(T object) { + return object.getCourse(); + } + }; + } + + public static SortableRaceListColumn getWindSourcesCountColumn() { + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator(false)) { + @Override + protected Integer getComparisonValue(T object) { + return object.getWindSourcesCount(); + } + }; + return new DataCountColumn(DataCountColumn.ICON_WIND, comparator) { + @Override + public Number getValue(T object) { + return object.getWindSourcesCount(); + } + }; + } + + public static SortableRaceListColumn getVideoCountColumn() { + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator(false)) { + @Override + protected Integer getComparisonValue(T object) { + return object.getVideoCount(); + } + }; + return new DataCountColumn(DataCountColumn.ICON_VIDEO, comparator) { + @Override + public Number getValue(T object) { + return object.getVideoCount(); + } + }; + } + + public static SortableRaceListColumn getAudioCountColumn() { + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator(false)) { + @Override + protected Integer getComparisonValue(T object) { + return object.getAudioCount(); + } + }; + return new DataCountColumn(DataCountColumn.ICON_AUDIO, comparator) { + @Override + public Number getValue(T object) { + return object.getAudioCount(); + } + }; + } + + public static SortableRaceListColumn getRaceViewStateColumn() { + Cell cell = new AbstractCell() { + @Override + public void render(Context context, T value, SafeHtmlBuilder sb) { + RaceViewState state = value.getViewState(); + RaceProgressDTO progress = value.getProgress(); + if (state == RaceViewState.SCHEDULED) { + double min = MillisecondsTimePoint.now().until(new MillisecondsTimePoint(value.getStart())).asMinutes(); + sb.appendEscaped(I18N.startingInMinutes((int) min)); + } else if (state == RaceViewState.RUNNING && progress != null) { + SafeStyles width = SafeStylesUtils.forWidth(progress.getPercentageProgress(), Unit.PCT); + String text = I18N.currentOfTotalLegs(progress.getCurrentLeg(), progress.getTotalLegs()); + sb.append(TEMPLATE.viewStateRunning(CSS.race_itemstatus_progressbar(), + CSS.race_itemstatus_progressbar_progress(), width, text)); + } else { + sb.appendEscaped(state.getLabel()); + } + } + }; + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NullSafeComparableComparator()) { + @Override + protected RaceViewState getComparisonValue(T object) { + return object.getViewState(); + } + }; + return new SortableRaceListColumn(I18N.status(), cell, comparator) { + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return CSS.race_item(); + } + + @Override + public T getValue(T object) { + return object; + } + }; + } + + public static SortableRaceListColumn getWinnerColumn() { + Cell cell = new AbstractCell() { + @Override + public void render(Context context, SimpleCompetitorDTO value, SafeHtmlBuilder sb) { + if (value != null) { + SafeUri flagImageUri = FlagImageResolver.getFlagImageUri(value.getFlagImageURL(), value.getTwoLetterIsoCountryCode()); + sb.append(TEMPLATE.winner(CSS.race_item_flag(), CSS.race_item_winner(), flagImageUri, value.getName())); + } + } + }; + InvertibleComparator comparator = new InvertibleComparatorWrapper(new NaturalComparator(false)) { + @Override + protected String getComparisonValue(T object) { + return object.getWinner() != null ? object.getWinner().getName() : null; + } + }; + return new SortableRaceListColumn(I18N.winner(), cell, comparator) { + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.race_itemwinner()); + } + + @Override + public SimpleCompetitorDTO getValue(T object) { + return object.getWinner(); + } + }; + } + + public static > SortableRaceListColumn getRaceViewerButtonColumn(final EventView.Presenter presenter) { + Cell cell = new AbstractCell() { + private final String watchNowStyle = getButtonStyleNames(MAIN_CSS.buttonred()); + private final String analyseRaceStyle = getButtonStyleNames(MAIN_CSS.buttonprimary()); + + @Override + public void render(Context context, T data, SafeHtmlBuilder sb) { + if (data.getTrackingState() != RaceTrackingState.TRACKED_VALID_DATA) { + SafeStylesBuilder styles = new SafeStylesBuilder().textTransform(TextTransform.UPPERCASE); + styles.trustedColor("#666").trustedNameAndValue("font-size", "0.933333333333333rem").toSafeStyles(); + styles.trustedNameAndValue("padding", "0.714285714285714em 1.071428571428571em"); + sb.append(TEMPLATE.raceNotTracked(styles.toSafeStyles(), I18N_UBI.eventRegattaRaceNotTracked())); + } else { + String styleNames = data.getViewState() == RaceViewState.FINISHED ? analyseRaceStyle : watchNowStyle; + String text = data.getViewState() == RaceViewState.FINISHED ? I18N.analyseRace() : I18N_UBI.watchNow(); + String raceViewerURL = presenter.getRaceViewerURL(data.getRegattaName(), data.getTrackedRaceName()); + sb.append(TEMPLATE.raceViewerLinkButton(styleNames, text, raceViewerURL)); + } + } + + private final String getButtonStyleNames(String buttonColor) { + return Util.join(" ", MAIN_CSS.button(), MAIN_CSS.buttonstrong(), buttonColor, MAIN_CSS.buttonarrowrightwhite()); + } + }; + InvertibleComparator comparator = null; + return new SortableRaceListColumn("", cell, comparator) { + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), CSS.raceslist_head_itembutton()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.race_itemright()); + } + + @Override + public T getValue(T object) { + return object; + } + }; + } + + public static class SortableRaceListStartTimeColumn> extends SortableRaceListColumn { + protected SortableRaceListStartTimeColumn(InvertibleComparator comparator) { + super(I18N.start(), new StartTimeCell(), comparator, SortingOrder.DESCENDING); + } + + public void setShowTimeOnly(boolean showTimeOnly) { + ((StartTimeCell) getCell()).showTimeOnly = showTimeOnly; + } + + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return CSS.race_item(); + } + + @Override + public Date getValue(T object) { + return object.getStart(); + } + + private static class StartTimeCell extends AbstractCell { + private static final DateTimeFormat DATE_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.MONTH_NUM_DAY); + private static final DateTimeFormat TIME_FORMAT = DateTimeFormat.getFormat(PredefinedFormat.HOUR24_MINUTE); + private boolean showTimeOnly = true; + @Override + public void render(Context context, Date value, SafeHtmlBuilder sb) { + if (value != null) { + sb.appendEscaped((showTimeOnly ? "" : DATE_FORMAT.format(value) + " ") + TIME_FORMAT.format(value)); + } + } + } + } + + + private static abstract class WindSpeedOrRangeColumn> extends SortableRaceListColumn { + protected WindSpeedOrRangeColumn(InvertibleComparator comparator) { + super(I18N.wind(), new TextCell(), comparator); + } + + @Override + public String getHeaderStyle() { + return CSS.raceslist_head_item(); + } + + @Override + public String getColumnStyle() { + return CSS.race_item(); + } + } + + private static abstract class DataCountColumn extends SortableRaceListColumn { + private static final SafeUri ICON_WIND = UriUtils.fromTrustedString("images/home/icon-wind.png"); + private static final SafeUri ICON_VIDEO = UriUtils.fromTrustedString("images/home/icon-video.png"); + private static final SafeUri ICON_AUDIO = UriUtils.fromTrustedString("images/home/icon-audio.png"); + + private DataCountColumn(final SafeUri imageUri, InvertibleComparator comparator) { + super(new SafeHtmlHeader(TEMPLATE.imageHeader(CSS.raceslist_head_itemflag(), imageUri)), new AbstractCell() { + @Override + public void render(Context context, Number value, SafeHtmlBuilder sb) { + if (value != null && value.longValue() > 0) { + sb.append(value.longValue()); + } else { + sb.appendHtmlConstant("—"); + } + } + }, comparator, SortingOrder.DESCENDING); + } + + @Override + public String getHeaderStyle() { + return getStyleNamesString(CSS.raceslist_head_item(), CSS.raceslist_head_itemcenter(), MEDIA_CSS.showonlarge()); + } + + @Override + public String getColumnStyle() { + return getStyleNamesString(CSS.race_item(), CSS.race_itemcenter(), MEDIA_CSS.showonlarge()); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListContainer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListContainer.java new file mode 100644 index 00000000000..ce01c433c86 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListContainer.java @@ -0,0 +1,71 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.racelist; + + +import java.util.Collection; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.HeadingElement; +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources; +import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshableWidget; +import com.sap.sailing.gwt.ui.shared.dispatch.CollectionResult; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; + +public class RaceListContainer> extends Composite implements RefreshableWidget> { + + private static RaceListContainerUiBinder uiBinder = GWT.create(RaceListContainerUiBinder.class); + + interface RaceListContainerUiBinder extends UiBinder> { + } + + @UiField HeadingElement titleUi; + @UiField DivElement infoUi; + @UiField DivElement noContentPlaceholderUi; + @UiField(provided = true) AbstractRaceList raceListUi; + private String noContentPlaceholderText; + + public RaceListContainer(String title, AbstractRaceList raceList) { + this(title, null, raceList); + } + + public RaceListContainer(String title, String noContentPlaceholderText, AbstractRaceList raceList) { + this.noContentPlaceholderText = noContentPlaceholderText; + RacesListLiveResources.INSTANCE.css().ensureInjected(); + this.raceListUi = raceList; + initWidget(uiBinder.createAndBindUi(this)); + this.titleUi.setInnerText(title); + raceListUi.setVisible(false); + } + + @Override + public void setData(CollectionResult data, long nextUpdate, int updateNo) { + setRaceListData(data == null ? null : data.getValues()); + } + + public void setRaceListData(Collection data) { + if(data == null || data.isEmpty()) { + if(noContentPlaceholderText == null) { + getElement().getStyle().setDisplay(Display.NONE); + } else { + noContentPlaceholderUi.setInnerText(noContentPlaceholderText); + noContentPlaceholderUi.getStyle().clearDisplay(); + raceListUi.setVisible(false); + } + } else { + noContentPlaceholderUi.getStyle().setDisplay(Display.NONE); + getElement().getStyle().clearDisplay(); + raceListUi.setVisible(true); + this.raceListUi.setTableData(data); + } + } + + public void setInfoText(String infoText) { + this.infoUi.setInnerText(infoText); + this.infoUi.getStyle().clearDisplay(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListContainer.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListContainer.ui.xml new file mode 100644 index 00000000000..d2319f51abe --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListContainer.ui.xml @@ -0,0 +1,14 @@ + + + + + + +

+
+
+ +
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListDataUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListDataUtil.java new file mode 100644 index 00000000000..aa3dfb86716 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/RaceListDataUtil.java @@ -0,0 +1,142 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.racelist; + +import java.util.Collection; + +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListRaceDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.SimpleCompetitorDTO; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.AbstractWindDTO; +import com.sap.sse.common.Duration; + +public class RaceListDataUtil { + + private interface ValueProvider { + V getValue(T object); + } + + private static > boolean hasValues(Collection races, ValueProvider valueProvider) { + for (T race : races) { + if (valueProvider.getValue(race) != null) { + return true; + } + } + return false; + } + + private static > boolean hasNumberValues(Collection races, ValueProvider valueProvider) { + for (T race : races) { + Number value = valueProvider.getValue(race); + if (value != null && value.longValue() > 0) { + return true; + } + } + return false; + } + + private static > boolean hasDifferentValues(Collection races, ValueProvider valueProvider) { + Object compareValue = null; + for (T race : races) { + Object currentValue = valueProvider.getValue(race); + if (currentValue == null) { + continue; + } else if (compareValue == null) { + compareValue = currentValue; + } else if (!compareValue.equals(currentValue)) { + return true; + } + } + return false; + } + + public static > boolean hasFleets(Collection data) { + return hasValues(data, new ValueProvider() { + @Override + public FleetMetadataDTO getValue(T object) { + return object.getFleet(); + } + }); + } + + public static boolean hasDurations(Collection data) { + return hasValues(data, new ValueProvider() { + @Override + public Duration getValue(T object) { + return object.getDuration(); + } + }); + } + + public static > boolean hasWind(Collection data) { + return hasValues(data, new ValueProvider() { + @Override + public AbstractWindDTO getValue(T object) { + return object.getWind(); + } + }); + } + + public static boolean hasWindSources(Collection data) { + return hasNumberValues(data, new ValueProvider() { + @Override + public Number getValue(T object) { + return object.getWindSourcesCount(); + } + }); + } + + public static boolean hasVideos(Collection data) { + return hasNumberValues(data, new ValueProvider() { + @Override + public Number getValue(T object) { + return object.getVideoCount(); + } + }); + } + + public static boolean hasAudios(Collection data) { + return hasNumberValues(data, new ValueProvider() { + @Override + public Number getValue(T object) { + return object.getAudioCount(); + } + }); + } + + public static > boolean hasCourses(Collection data) { + return hasValues(data, new ValueProvider() { + @Override + public String getValue(T object) { + return object.getCourse(); + } + }); + } + + public static > boolean hasCourseAreas(Collection data) { + return hasValues(data, new ValueProvider() { + @Override + public String getValue(T object) { + return object.getCourseArea(); + } + }); + } + + public static boolean hasWinner(Collection data) { + return hasValues(data, new ValueProvider() { + @Override + public SimpleCompetitorDTO getValue(T object) { + return object.getWinner(); + } + }); + } + + private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24; + public static > boolean hasDifferentStartDates(Collection data) { + return hasDifferentValues(data, new ValueProvider() { + @Override + public Long getValue(T object) { + return object.getStart().getTime() / DAY_IN_MILLIS; + } + }); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/SortableRaceListColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/SortableRaceListColumn.java new file mode 100644 index 00000000000..79239a0c831 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/SortableRaceListColumn.java @@ -0,0 +1,57 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.racelist; + +import com.google.gwt.cell.client.Cell; +import com.google.gwt.user.cellview.client.Header; +import com.google.gwt.user.cellview.client.TextHeader; +import com.sap.sailing.domain.common.InvertibleComparator; +import com.sap.sailing.domain.common.SortingOrder; +import com.sap.sailing.gwt.ui.client.shared.controls.SortableColumn; +import com.sap.sse.common.Util; + +public abstract class SortableRaceListColumn extends SortableColumn { + + private final Header header; + private final InvertibleComparator comparator; + + private boolean showDetails = true; + + protected SortableRaceListColumn(String headerText, Cell cell, InvertibleComparator comparator) { + this(new TextHeader(headerText), cell, comparator); + } + + protected SortableRaceListColumn(String headerText, Cell cell, InvertibleComparator comparator, SortingOrder preferredSortingOrder) { + this(new TextHeader(headerText), cell, comparator, preferredSortingOrder); + } + + protected SortableRaceListColumn(Header header, Cell cell, InvertibleComparator comparator) { + this(header, cell, comparator, SortingOrder.ASCENDING); + } + + protected SortableRaceListColumn(Header header, Cell cell, InvertibleComparator comparator, SortingOrder preferredSortingOrder) { + super(cell, preferredSortingOrder); + this.header = header; + this.comparator = comparator; + } + + @Override + public final InvertibleComparator getComparator() { + return comparator; + } + + @Override + public final Header getHeader() { + return header; + } + + public void setShowDetails(boolean showDetails) { + this.showDetails = showDetails; + } + + public boolean isShowDetails() { + return showDetails; + } + + protected final String getStyleNamesString(String... styleNames) { + return Util.join(" ", styleNames); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.css deleted file mode 100644 index 9a134001372..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.css +++ /dev/null @@ -1,79 +0,0 @@ -/** - * The header of a regatta of an event - */ -.regattaheader { - font-size: 1rem; - margin-top: 2em; - padding: 0.666666666666667em; - overflow: hidden; - border-radius: 0.333333333333333em; - border: 1px solid #dedede; - background-color: #f2f2f2; -} -.regattaheaderlink { - display: block; - margin-top: 0; - border: none; - border-radius: 0; - color: #0a0a0a; -} -.regattaheader_logo { - float: left; - width: 4em; - height: 3.333333333333333em; - margin-right: 0.666666666666667em; - text-align: center; - line-height: 3.333333333333333em; - border-right: 1px solid #ccc; - padding-right: 0.666666666666667em; -} -.regattaheader_logo img { - display: inline-block; - text-align: center; - vertical-align: middle; -} -.regattaheader_content { - float: left; - width: 80%; - overflow: hidden; -} -.regattaheader_content_title { - font-size: 1.466666666666667em; - font-weight: 700; - height: 1.363636363636364em; - line-height: 1.363636363636364em; -} -.regattaheader_content_details { - height: 1.333333333333333em; -} -.regattaheader_content_details_item { - float: left; - font-size: 13px; - font-weight: 600; - color: #666; - padding: 0 0.666666666666667em; - border-right: 1px solid #ccc; -} -.regattaheader_content_details_item:first-of-type { - padding-left: 0; -} -.regattaheader_content_details_item:last-of-type { - border: none; - padding-right: 0; -} -.regattaheader_arrow { - float: right; - width: 5%; - line-height: 3.333333333333333em; - text-align: right; -} -.regattaheader_arrow:before { - content: ''; - display: inline-block; - margin-left: 0.333333333333333em; - width: 0.8em; - height: 1.333333333333333em; - background-image: url("../images/home/breadcrumb-arrow.png"); - background-size: contain; - background-repeat: no-repeat; -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.gss new file mode 100644 index 00000000000..52b6701c6da --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.gss @@ -0,0 +1,125 @@ +/** + * The header of a regatta of an event + */ +.regattaheader { + width: 100%; + float: left; + font-size: 1rem; + padding: 0.666666666666667em; + padding-right: 1em; + overflow: hidden; + color: #0a0a0a; + background-color: #f2f2f2; + border-bottom: 1px solid #ddd; +} +.regattaheaderpadding { + padding-right: 5em; +} +.regattaheader_logo { + float: left; + width: 3.333333333333333em; + height: 3.333333333333333em; + text-align: center; + line-height: 3.333333333333333em; + background-size: contain; + background-position: center; + background-repeat: no-repeat; +} +.regattaheader_logo img { + display: inline-block; + text-align: center; + vertical-align: middle; +} +.regattaheader_content { + float: left; + width: 60%; + overflow: hidden; + border-left: 1px solid #ccc; + margin-left: 0.666666666666667em; + padding-left: 1em; +} +.regattaheader_content_title { + font-size: 1.466666666666667em; + font-weight: 700; + height: 1.363636363636364em; + line-height: 1.363636363636364em; +} +.regattaheader_content_details { + height: 1.333333333333333em; +} +.regattaheader_content_details_item { + float: left; + font-size: 13px; + font-weight: 600; + color: #666; + padding: 0 0.666666666666667em; + border-right: 1px solid #ccc; +} +.regattaheader_content_details_item:first-of-type { + padding-left: 0; +} +.regattaheader_content_details_item:last-of-type { + border: none; + padding-right: 0; +} +.regattaheader_toggle { + float: right; + width: 4em; + cursor: pointer; + margin: 0.666666666666667em 0 0.666666666666667em -4em; + line-height: 3.333333333333333em; + text-align: center; + padding: 0 0.666666666666667em; + border-left: 1px solid #ccc; +} +.regattaheader_toggle_icon { + padding: 1em 0; + width: 1.333333333333333em; + height: 1.333333333333333em; + -webkit-transform: rotate(180deg); + -moz-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} +.standings_listhidden .regattaheader_toggle_icon { + -webkit-transform: rotate(0deg); + -moz-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); +} +.regattaheader_arrow { + position: absolute; + top: 0; + right: 0; + bottom: 0; + text-align: right; + padding: 0.666666666666667em; + padding-right: 4.666666666666667em; + background-image: url("images/home/arrow-down-grey.png"); + background-size: 1.333333333333333em 1.333333333333333em; + background-position: center; + background-repeat: no-repeat; + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + transform: rotate(-90deg); +} +.regattaheader_update { + width: 20%; + float: right; + padding: 0.533333333333333em 0; + text-align: right; + font-weight: 600; + color: #666; +} +.regattaheader_update_title { + text-transform: uppercase; +} +img.regattaheader_update_title_icon { + height: 1.2em; + margin-right: 0.333333333333333em; + vertical-align: bottom; +} +.regattaheader_update_timestamp { + font-size: 12px; +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.java index 50e553081e1..74ff4e8f089 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.java @@ -1,26 +1,33 @@ package com.sap.sailing.gwt.home.client.place.event.partials.regattaHeader; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.AnchorElement; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.gwt.ui.client.StringMessages; -import com.sap.sailing.gwt.ui.shared.eventview.RegattaReferenceDTO; +import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; +import com.sap.sailing.gwt.ui.shared.eventview.RegattaMetadataDTO; public class RegattaHeader extends Composite { - private static EventHeaderUiBinder uiBinder = GWT.create(EventHeaderUiBinder.class); - interface EventHeaderUiBinder extends UiBinder { + private static RegattaHeaderUiBinder uiBinder = GWT.create(RegattaHeaderUiBinder.class); + + interface RegattaHeaderUiBinder extends UiBinder { } - @UiField StringMessages i18n; - - - public RegattaHeader(RegattaReferenceDTO regattaRefDTO) { - - RegattaHeaderResources.INSTANCE.css().ensureInjected(); + @UiField AnchorElement headerBodyUi; + @UiField AnchorElement headerArrowUi; + + public RegattaHeader(RegattaMetadataDTO regattaMetadata) { initWidget(uiBinder.createAndBindUi(this)); + headerBodyUi.appendChild(new RegattaHeaderBody(regattaMetadata).getElement()); } - + + public void setRegattaNavigation(PlaceNavigation placeNavigation) { + headerArrowUi.getStyle().clearDisplay(); + placeNavigation.configureAnchorElement(headerBodyUi); + placeNavigation.configureAnchorElement(headerArrowUi); + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.ui.xml index 1304c8dd297..0d8db0c4e7d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeader.ui.xml @@ -1,30 +1,23 @@ - - - - - - - -
-
-
- - - - - - -
-
-
- -
-
-
-
+ + + + + +
+
+
+ + + + + + + + +
+
+
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.java new file mode 100644 index 00000000000..a273d872cb4 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.java @@ -0,0 +1,62 @@ +package com.sap.sailing.gwt.home.client.place.event.partials.regattaHeader; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.SpanElement; +import com.google.gwt.resources.client.ImageResource; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.UIObject; +import com.sap.sailing.gwt.common.client.BoatClassImageResolver; +import com.sap.sailing.gwt.home.client.shared.LabelTypeUtil; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.eventview.RegattaMetadataDTO; + +public class RegattaHeaderBody extends UIObject { + + private static final StringMessages I18N = StringMessages.INSTANCE; + private static RegattaHeaderBodyUiBinder uiBinder = GWT.create(RegattaHeaderBodyUiBinder.class); + + interface RegattaHeaderBodyUiBinder extends UiBinder { + } + + @UiField protected DivElement logoUi; + @UiField protected SpanElement nameUi; + @UiField protected DivElement labelUi; + @UiField protected DivElement detailsItemContainerUi; + + public RegattaHeaderBody(RegattaMetadataDTO regattaMetadata) { + RegattaHeaderResources.INSTANCE.css().ensureInjected(); + setElement(uiBinder.createAndBindUi(this)); + ImageResource logo = BoatClassImageResolver.getBoatClassIconResource(regattaMetadata.getBoatClass()); + logoUi.getStyle().setBackgroundImage("url('" + logo.getSafeUri().asString() + "')"); + nameUi.setInnerText(regattaMetadata.getDisplayName()); + LabelTypeUtil.renderLabelTypeOrHide(labelUi, regattaMetadata.getState().getStateMarker()); + addDetailsItem(regattaMetadata.getCompetitorsCount(), I18N.competitorsCount(regattaMetadata.getCompetitorsCount())); + addDetailsItem(regattaMetadata.getRaceCount(), I18N.racesCount(regattaMetadata.getRaceCount())); + addDetailsItem(regattaMetadata.getTrackedRacesCount(), I18N.trackedRacesCount(regattaMetadata.getTrackedRacesCount())); + String defaultCourseAreaName = regattaMetadata.getDefaultCourseAreaName(); + if(defaultCourseAreaName != null) { + addDetailsItem(I18N.courseAreaName(defaultCourseAreaName)); + } + addDetailsItem(regattaMetadata.getBoatCategory()); + } + + private void addDetailsItem(int count, String text) { + if (count > 0) { + addDetailsItem(text); + } + } + + private void addDetailsItem(String text) { + if(text != null) { + DivElement detailsItem = DOM.createDiv().cast(); + detailsItem.addClassName(RegattaHeaderResources.INSTANCE.css().regattaheader_content_details_item()); + detailsItem.setInnerText(text); + detailsItemContainerUi.appendChild(detailsItem); + } + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.ui.xml index dac8b67d8b1..c901052f007 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderBody.ui.xml @@ -1,59 +1,24 @@ - - - - - - - -
- -
-
-
- - -
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
-
+ + + + +
+
+
+
+ +
+
+
+ +
+
+ +
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderResources.java index 5234b07d59b..30dd5cf7f89 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/regattaHeader/RegattaHeaderResources.java @@ -7,17 +7,24 @@ import com.google.gwt.resources.client.CssResource; public interface RegattaHeaderResources extends ClientBundle { public static final RegattaHeaderResources INSTANCE = GWT.create(RegattaHeaderResources.class); - @Source("RegattaHeader.css") + @Source("RegattaHeader.gss") LocalCss css(); public interface LocalCss extends CssResource { String regattaheader(); - String regattaheaderlink(); + String regattaheaderpadding(); String regattaheader_logo(); String regattaheader_content(); String regattaheader_content_title(); String regattaheader_content_details(); String regattaheader_content_details_item(); + String regattaheader_toggle(); + String regattaheader_toggle_icon(); + String standings_listhidden(); String regattaheader_arrow(); + String regattaheader_update(); + String regattaheader_update_title(); + String regattaheader_update_title_icon(); + String regattaheader_update_timestamp(); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java index f639c97ab56..48b4daee058 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java @@ -48,7 +48,7 @@ public class RegattaOverviewTabView extends Composite implements RegattaTabView< RefreshManager refreshManager = new RefreshManager(this, currentPresenter.getDispatch()); stage.setupRefresh(refreshManager); - refreshManager.add(racesListLive, new GetLiveRacesForRegattaAction(currentPresenter.getCtx().getEventDTO() + refreshManager.add(racesListLive.getRefreshable(), new GetLiveRacesForRegattaAction(currentPresenter.getCtx().getEventDTO() .getId(), currentPresenter.getCtx().getRegattaId())); contentArea.setWidget(this); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.java index 33cec046313..230705c8bb2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.java @@ -1,29 +1,81 @@ package com.sap.sailing.gwt.home.client.place.event.regatta.tabs; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Style.Display; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AcceptsOneWidget; import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.common.client.controls.tabbar.TabView; +import com.sap.sailing.gwt.home.client.place.event.EventView; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.ListNavigationPanel; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.ListNavigationPanel.ListNavigationAction; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.ListNavigationPanel.SelectionCallback; +import com.sap.sailing.gwt.home.client.place.event.partials.listNavigation.RaceStateLegend; +import com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList.MultiRegattaListItem; +import com.sap.sailing.gwt.home.client.place.event.partials.multiRegattaList.MultiRegattaListStepsLegend; +import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLive; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.AbstractRaceList; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListColumnFactory; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListContainer; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.RaceListDataUtil; +import com.sap.sailing.gwt.home.client.place.event.partials.racelist.SortableRaceListColumn; import com.sap.sailing.gwt.home.client.place.event.partials.regattaraces.EventRegattaRaces; import com.sap.sailing.gwt.home.client.place.event.regatta.EventRegattaView.Presenter; import com.sap.sailing.gwt.home.client.place.event.regatta.RegattaTabView; +import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshManager; +import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshableWidget; +import com.sap.sailing.gwt.home.shared.ExperimentalFeatures; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; import com.sap.sailing.gwt.ui.shared.RaceGroupDTO; import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.event.GetFinishedRacesAction; +import com.sap.sailing.gwt.ui.shared.dispatch.event.GetLiveRacesForRegattaAction; +import com.sap.sailing.gwt.ui.shared.dispatch.event.GetRegattaWithProgressAction; +import com.sap.sailing.gwt.ui.shared.dispatch.event.RaceListRaceDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; import com.sap.sse.common.Util.Triple; /** * Created by pgtaboada on 25.11.14. */ public class RegattaRacesTabView extends Composite implements RegattaTabView { + + private static final StringMessages I18N = StringMessages.INSTANCE; + + private enum Navigation implements ListNavigationAction { + SORT_LIST_FORMAT("Sortable list Format TODO", false), + COMPETITION_FORMAT("Competition Format TODO", true); + + private final String displayName; + private final boolean showAdditionalWidget; + + private Navigation(String displayName, boolean showAdditionalWidget) { + this.displayName = displayName; + this.showAdditionalWidget = showAdditionalWidget; + } + + @Override + public String getDisplayName() { + return displayName; + } + + @Override + public boolean isShowAdditionalWidget() { + return showAdditionalWidget; + } + } interface MyBinder extends UiBinder { } @@ -32,17 +84,28 @@ public class RegattaRacesTabView extends Composite implements RegattaTabView listNavigationPanelUi; + @UiField FlowPanel listFormatContainerUi; + @UiField FlowPanel compFormatContainerUi; + @UiField SimplePanel oldContentContainer; + + private RacesListLive liveRacesList; + private RaceListContainer raceListContainer; public RegattaRacesTabView() { - + listNavigationPanelUi = new ListNavigationPanel(new RegattaRacesTabViewNavigationSelectionCallback()); + listNavigationPanelUi.setAdditionalWidget(new RaceStateLegend()); + initWidget(ourUiBinder.createAndBindUi(RegattaRacesTabView.this)); } - + @Override public void setPresenter(Presenter currentPresenter) { this.currentPresenter = currentPresenter; - + listFormatContainerUi.add(liveRacesList = new RacesListLive(currentPresenter, false)); + raceListContainer = new RaceListContainer<>(I18N.finishedRaces(), I18N.noFinishedRaces(), new RaceListFinishedRaces(currentPresenter)); + listFormatContainerUi.add(raceListContainer); } @Override @@ -52,52 +115,56 @@ public class RegattaRacesTabView extends Composite implements RegattaTabView>() { - @Override - public void onSuccess(List raceGroups) { - if (raceGroups.size() > 0) { - Map> regattaStructure = getRegattaStructure(); - - Triple selectedRegatta = regattaStructure - .get(selectedRegattaId); - - regattaRaces = new EventRegattaRaces(currentPresenter); - - regattaRaces.setRaces(selectedRegatta.getC(), false, selectedRegatta.getB(), - selectedRegatta.getA()); - - // if (eventDTO.isRunning()) { - // // create update time for race states only for running events - // serverUpdateTimer.addTimeListener(new TimeListener() { - // @Override - // public void timeChanged(Date newTime, Date oldTime) { - // loadAndUpdateEventRaceStatesLog(); - // } - // }); - // } - initWidget(ourUiBinder.createAndBindUi(RegattaRacesTabView.this)); - contentArea.setWidget(RegattaRacesTabView.this); - } else { - // createEventWithoutRegattasView(event, panel); - } + RefreshManager refreshManager = new RefreshManager(this, currentPresenter.getDispatch()); + if (ExperimentalFeatures.SHOW_NEW_RACES_LIST) { + listNavigationPanelUi.removeFromParent(); // TODO temporary removed + // listNavigationPanelUi.addAction(Navigation.SORT_LIST_FORMAT, true); + // listNavigationPanelUi.addAction(Navigation.COMPETITION_FORMAT, false); + + refreshManager.add(liveRacesList.getRefreshable(), new GetLiveRacesForRegattaAction(myPlace.getCtx().getEventDTO().getId(), myPlace.getRegattaId())); + refreshManager.add(new RefreshableWidget() { + @Override + public void setData(RegattaWithProgressDTO data, long nextUpdate, int updateNo) { + regattaInfoContainerUi.setWidget(new MultiRegattaListItem(data)); + } + }, new GetRegattaWithProgressAction(myPlace.getCtx().getEventDTO().getId(), myPlace.getRegattaId())); + refreshManager.add(raceListContainer, new GetFinishedRacesAction(myPlace.getCtx().getEventDTO().getId(), myPlace.getRegattaId())); + + oldContentContainer.removeFromParent(); + } else { + regattaProgressLegendUi.removeFromParent(); + regattaInfoContainerUi.removeFromParent(); + listFormatContainerUi.removeFromParent(); + compFormatContainerUi.removeFromParent(); + // TODO: understand, and than move this into appropiate place (probably context) + final String selectedRegattaId = myPlace.getCtx().getRegattaId(); + currentPresenter.ensureRegattaStructure(new AsyncCallback>() { + @Override + public void onSuccess(List raceGroups) { + if (raceGroups.size() > 0) { + Map> rs = getRegattaStructure(); + Triple selectedRegatta = rs .get(selectedRegattaId); + EventRegattaRaces regattaRaces = new EventRegattaRaces(currentPresenter); + oldContentContainer.setWidget(regattaRaces); + regattaRaces.setRaces(selectedRegatta.getC(), false, selectedRegatta.getB(), selectedRegatta.getA()); + } else { + // createEventWithoutRegattasView(event, panel); } + } - @Override - public void onFailure(Throwable caught) { - // createErrorView( - // "Error while loading the regatta structure with service getRegattaStructureOfEvent()", - // caught, panel); - } - }); + @Override + public void onFailure(Throwable caught) { + // createErrorView( + // "Error while loading the regatta structure with service getRegattaStructureOfEvent()", + // caught, panel); + } + }); + } + contentArea.setWidget(RegattaRacesTabView.this); } - + @Override public void stop() { - } @Override @@ -109,7 +176,7 @@ public class RegattaRacesTabView extends Composite implements RegattaTabView getPlaceClassForActivation() { return RegattaRacesPlace.class; } - + private Map> getRegattaStructure() { Map> result = new HashMap<>(); Map raceGroupsMap = new HashMap<>(); @@ -127,4 +194,62 @@ public class RegattaRacesTabView extends Composite implements RegattaTabView { + @Override + public void onSelectAction(Navigation action) { + showWidget(listFormatContainerUi, action == Navigation.SORT_LIST_FORMAT); + showWidget(compFormatContainerUi, action == Navigation.COMPETITION_FORMAT); + } + + private void showWidget(Widget widget, boolean show) { + widget.getElement().getStyle().setDisplay(show ? Display.BLOCK : Display.NONE); + } + } + + private class RaceListFinishedRaces extends AbstractRaceList { + private final SortableRaceListColumn durationColumn = RaceListColumnFactory.getDurationColumn(); + private final SortableRaceListColumn windSpeedColumn = RaceListColumnFactory.getWindRangeColumn(); + private final SortableRaceListColumn windSourcesCountColumn = RaceListColumnFactory.getWindSourcesCountColumn(); + private final SortableRaceListColumn videoCountColumn = RaceListColumnFactory.getVideoCountColumn(); + private final SortableRaceListColumn audioCountColumn = RaceListColumnFactory.getAudioCountColumn(); + private final SortableRaceListColumn winnerColumn = RaceListColumnFactory.getWinnerColumn(); + + public RaceListFinishedRaces(EventView.Presenter presenter) { + super(presenter); + } + + @Override + protected void setTableData(Collection data) { + boolean hasFleets = RaceListDataUtil.hasFleets(data); + this.fleetCornerColumn.setShowDetails(hasFleets); + this.fleetNameColumn.setShowDetails(hasFleets); + this.startTimeColumn.setShowTimeOnly(!RaceListDataUtil.hasDifferentStartDates(data)); + this.durationColumn.setShowDetails(RaceListDataUtil.hasDurations(data)); + boolean hasWind = RaceListDataUtil.hasWind(data); + this.windSpeedColumn.setShowDetails(hasWind); + this.windDirectionColumn.setShowDetails(hasWind); + this.windSourcesCountColumn.setShowDetails(RaceListDataUtil.hasWindSources(data)); + this.videoCountColumn.setShowDetails(RaceListDataUtil.hasVideos(data)); + this.audioCountColumn.setShowDetails(RaceListDataUtil.hasAudios(data)); + this.winnerColumn.setShowDetails(RaceListDataUtil.hasWinner(data)); + super.setTableData(data); + } + + @Override + protected void initTableColumns() { + add(fleetCornerColumn); + add(raceNameColumn); + add(fleetNameColumn); + add(startTimeColumn); + add(durationColumn); + add(windSpeedColumn); + add(windDirectionColumn); + add(windSourcesCountColumn); + add(videoCountColumn); + add(audioCountColumn); + add(winnerColumn); + add(raceViewerButtonColumn); + } + } + } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.ui.xml index aa3273ec11a..156a55fc060 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaRacesTabView.ui.xml @@ -1,8 +1,26 @@ - + + + + + .listContainer > div { + margin-bottom: 2em; + } + .legend { + margin-top: 2em; + margin-bottom: -1.5em; + } + - +
+ + +
+ +
+ + +
+
\ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/reload/RefreshManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/reload/RefreshManager.java index dbfd4b8065a..e2d4cd16ba7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/reload/RefreshManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/reload/RefreshManager.java @@ -114,13 +114,13 @@ public class RefreshManager { } @SuppressWarnings({ "rawtypes", "unchecked" }) - public >> void add(RefreshableWidget widget, + public >> void add(RefreshableWidget widget, ActionProvider provider) { refreshables.add(new RefreshHolder(widget, provider)); reschedule(); } - public >> void add(RefreshableWidget widget, A action) { + public >> void add(RefreshableWidget widget, A action) { add(widget, new DefaultActionProvider<>(action)); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regattaleaderboard/EventRegattaLeaderboard.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regattaleaderboard/EventRegattaLeaderboard.gss index 073eec8b81a..84b86a3f74a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regattaleaderboard/EventRegattaLeaderboard.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regattaleaderboard/EventRegattaLeaderboard.gss @@ -112,6 +112,11 @@ .regattaleaderboard_meta_reload:hover { background-color: #909090; } + +.regattaleaderboard_meta_reload_live:hover { + background-color: #ffa4a4; +} + .regattaleaderboard_meta_settings { display: inline-block; text-align: right; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/eventheader/EventHeader.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/eventheader/EventHeader.java index c04e7b8e0bd..c467508665f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/eventheader/EventHeader.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/eventheader/EventHeader.java @@ -47,7 +47,7 @@ public class EventHeader extends Composite { } LabelTypeUtil.renderLabelType(eventStateUi, event.getState().getStateMarker()); LogoUtil.setEventLogo(eventLogoUi, event); - eventDateUi.setInnerText(EventDatesFormatterUtil.formatDateRangeWithoutYear(event.getStartDate(), event.getEndDate())); + eventDateUi.setInnerText(EventDatesFormatterUtil.formatDateRangeWithYear(event.getStartDate(), event.getEndDate())); eventLocationUi.setInnerText(event.getLocationAndVenueAndCountry()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.gss index 6bcb034911b..70cd072f687 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.gss @@ -1,13 +1,15 @@ .sitefooter { - font-size: 0.8rem; background: #333; - padding: 2.5em; - margin-top: -13.916666666666666em; + padding: 5em 3em; + margin-top: -10em; color: #fff; text-align: center; + font-size: 110%; } .sitefooter a { color: #fff; + line-height: 2; + padding: 5px; } .sitefooter_language { display: block; @@ -20,5 +22,5 @@ a.sitefooter_language_linkactive { } .sitefooter_links { font-weight: 600; - padding: 2.083333333333333em 0; + padding: 3em 0; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.ui.xml index 3771f0fd98f..d15e5dda458 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/footer/Footer.ui.xml @@ -32,7 +32,7 @@ url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgaGVpZ2h0PScxOHB4JyB3aWR0aD0nMTZweCc+PHRleHQgeD0nMCcgeT0nMTUnIGZpbGw9JyNlZmFhMDAnIGZvbnQtc2l6ZT0nMTYnPiYjOTY2MDs8L3RleHQ+PC9zdmc+"); background-position: right center; background-repeat: no-repeat; - font-size: 0.8rem; + font-size: 110%; } @@ -58,8 +58,8 @@ - | + | @@ -69,14 +69,14 @@ href="mailto:sailing_analytics%40sap.com?subject=[SAP Sailing] Feedback" title="{i18n.footerFeedback}"> - | + | Desktop - | + | { } + + private final StringMessages I18N = StringMessages.INSTANCE; @UiField MobileSection itemContainerUi; @UiField SectionHeaderContent headerUi; - public MinileaderboardBox() { + public MinileaderboardBox(boolean isOverall) { initWidget(uiBinder.createAndBindUi(this)); + headerUi.setSectionTitle(isOverall ? I18N.overallStandings() : I18N.results()); headerUi.setInfoText(StringMessages.INSTANCE.details()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBox.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBox.ui.xml index 0a11d2d549f..391b63bff6e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBox.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/minileaderboard/MinileaderboardBox.ui.xml @@ -9,7 +9,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/sectionHeader/SectionHeader.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/sectionHeader/SectionHeader.gss index 9e55aefa556..f9854757a6e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/sectionHeader/SectionHeader.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/sectionHeader/SectionHeader.gss @@ -34,8 +34,6 @@ margin: -1em 1em -1em 0; background-size: contain; background-position: center; - -webkit-filter: grayscale(100%); - filter: grayscale(100%); } .sectionheader_item_image_seperator { position: absolute; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/EventViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/EventViewImpl.java index 9fb11bde045..fdd0da6baf7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/EventViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/event/EventViewImpl.java @@ -81,7 +81,7 @@ public class EventViewImpl extends Composite implements EventView { listContentUi.add(regattaStatus); refreshManager.add(regattaStatus, new GetRegattasAndLiveRacesForEventAction(event.getId())); } else { - MinileaderboardBox miniLeaderboard = new MinileaderboardBox(); + MinileaderboardBox miniLeaderboard = new MinileaderboardBox(false); miniLeaderboard.setAction(MSG.showAll(), currentPresenter.getRegattaMiniLeaderboardNavigation(regattaId)); listContentUi.add(miniLeaderboard); refreshManager.add(miniLeaderboard, new GetMiniLeaderbordAction(event.getId(), regattaId, 3)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/minileaderboard/MiniLeaderboardViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/minileaderboard/MiniLeaderboardViewImpl.java index c60dc80db34..291cc3f9a21 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/minileaderboard/MiniLeaderboardViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/minileaderboard/MiniLeaderboardViewImpl.java @@ -30,7 +30,7 @@ public class MiniLeaderboardViewImpl extends Composite implements MiniLeaderboar private Presenter currentPresenter; @UiField(provided = true) EventHeader eventHeaderUi; - @UiField + @UiField(provided = true) MinileaderboardBox minileaderboardUi; @UiField Quickfinder quickFinderUi; @@ -41,6 +41,7 @@ public class MiniLeaderboardViewImpl extends Composite implements MiniLeaderboar if(presenter.getCtx().getEventDTO().getType() == EventType.MULTI_REGATTA) { regattaDisplayName = presenter.getCtx().getRegatta().getDisplayName(); } + minileaderboardUi = new MinileaderboardBox(false); eventHeaderUi = new EventHeader(presenter.getCtx().getEventDTO(), regattaDisplayName); initWidget(uiBinder.createAndBindUi(this)); RefreshManager refreshManager = new RefreshManager(this, currentPresenter.getDispatch()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/SeriesViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/SeriesViewImpl.java index d8956b3cbf4..43b873e76c3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/SeriesViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/SeriesViewImpl.java @@ -29,7 +29,7 @@ public class SeriesViewImpl extends Composite implements SeriesView { @UiField(provided = true) SeriesHeader eventHeaderUi; @UiField Quickfinder quickFinderUi; - @UiField MinileaderboardBox leaderboardUi; + @UiField(provided = true) MinileaderboardBox leaderboardUi; @UiField FlowPanel eventsUi; // @UiField(provided = true) StatisticsBox statisticsBoxUi; @@ -42,6 +42,7 @@ public class SeriesViewImpl extends Composite implements SeriesView { EventSeriesViewDTO series = currentPresenter.getCtx().getSeriesDTO(); eventHeaderUi = new SeriesHeader(series); // this.setupStatisticsBox(event); + leaderboardUi = new MinileaderboardBox(true); initWidget(uiBinder.createAndBindUi(this)); this.setupListContent(series); this.setupEventListContent(series); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/minileaderboard/SeriesMiniOverallLeaderboardViewImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/minileaderboard/SeriesMiniOverallLeaderboardViewImpl.java index eb7d6e1394c..31af84116a8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/minileaderboard/SeriesMiniOverallLeaderboardViewImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/places/series/minileaderboard/SeriesMiniOverallLeaderboardViewImpl.java @@ -29,11 +29,12 @@ public class SeriesMiniOverallLeaderboardViewImpl extends Composite implements S @UiField(provided = true) SeriesHeader eventHeaderUi; @UiField Quickfinder quickFinderUi; - @UiField + @UiField(provided = true) MinileaderboardBox minileaderboardUi; public SeriesMiniOverallLeaderboardViewImpl(Presenter presenter) { this.currentPresenter = presenter; + minileaderboardUi = new MinileaderboardBox(true); eventHeaderUi = new SeriesHeader(presenter.getCtx().getSeriesDTO()); initWidget(uiBinder.createAndBindUi(this)); RefreshManager refreshManager = new RefreshManager(this, currentPresenter.getDispatch()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/ExperimentalFeatures.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/ExperimentalFeatures.java new file mode 100644 index 00000000000..4043aa55112 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/ExperimentalFeatures.java @@ -0,0 +1,18 @@ +package com.sap.sailing.gwt.home.shared; + +/** + * This class contains convenience boolean flags to enable/ disable features in experimental phase. + */ +public class ExperimentalFeatures { + + /** + * Show new races list + */ + public static final boolean SHOW_NEW_RACES_LIST = true; + /** + * Show new regatta list + */ + public static final boolean SHOW_NEW_REGATTA_LIST = true; + + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/SwitchingEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/SwitchingEntryPoint.java index f7d0cf8138c..686007a75a3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/SwitchingEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/SwitchingEntryPoint.java @@ -152,7 +152,7 @@ public class SwitchingEntryPoint implements EntryPoint { } private void configureDesktopHeader() { - metaElement("viewport", "width=device-width,initial-scale=0.5,maximum-scale=1"); + metaElement("viewport", "width=device-width,initial-scale=0.5,maximum-scale=2"); } private void configureMobileHeader() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java index 4571c89fdd0..66bfacb9f73 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewEntryPoint.java @@ -28,6 +28,7 @@ public class RegattaOverviewEntryPoint extends AbstractSailingEntryPoint { private final static String PARAM_ONLY_RACES_OF_SAME_DAY = "onlyracesofsameday"; private final static String PARAM_REGATTA = "regatta"; private final static String PARAM_COURSE_AREA = "coursearea"; + private final static String PARAM_IGNORE_LOCAL_SETTINGS = "ignoreLocalSettings"; private DockLayoutPanel containerPanel; private RaceDetailPanel detailPanel; @@ -96,7 +97,8 @@ public class RegattaOverviewEntryPoint extends AbstractSailingEntryPoint { private void createAndAddRegattaPanel(UUID eventId) { RegattaRaceStatesSettings settings = createRegattaRaceStatesSettingsFromURL(); - regattaPanel = new RegattaOverviewPanel(sailingService, this, getStringMessages(), eventId, settings, userAgent); + boolean ignoreLocalSettings = getIgnoreLocalSettingsFromURL(); + regattaPanel = new RegattaOverviewPanel(sailingService, this, getStringMessages(), eventId, settings, userAgent, ignoreLocalSettings); regattaPanel.addHandler(new EventDTOLoadedEvent.Handler() { @Override @@ -120,6 +122,10 @@ public class RegattaOverviewEntryPoint extends AbstractSailingEntryPoint { containerPanel.add(scrollPanel); } + private boolean getIgnoreLocalSettingsFromURL() { + return Boolean.parseBoolean(Window.Location.getParameter(PARAM_IGNORE_LOCAL_SETTINGS)); + } + private void createAndAddDetailPanel() { detailPanel = new RaceDetailPanel(getStringMessages(), new ClickHandler() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java index 46fe03b9a73..4d4de5230a4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaOverviewPanel.java @@ -81,7 +81,8 @@ public class RegattaOverviewPanel extends SimplePanel { final StringMessages stringMessages, UUID eventId, RegattaRaceStatesSettings settings, - UserAgentDetails userAgent) { + UserAgentDetails userAgent, + boolean ignoreLocalSettings) { this.sailingService = sailingService; this.stringMessages = stringMessages; @@ -102,7 +103,7 @@ public class RegattaOverviewPanel extends SimplePanel { mainPanel.setWidth("100%"); mainPanel.addStyleName(style.contentWrapper()); - regattaRaceStatesComponent = new RegattaRaceStatesComponent(sailingService, errorReporter, stringMessages, eventId, settings, uiUpdateTimer); + regattaRaceStatesComponent = new RegattaRaceStatesComponent(sailingService, errorReporter, stringMessages, eventId, settings, uiUpdateTimer, ignoreLocalSettings); this.eventRaceGroupListeners.add(regattaRaceStatesComponent); regattaRaceStatesComponent.setWidth("100%"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java index e089c73e226..efdf40a45fa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java @@ -125,6 +125,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component private CellTable table; boolean hasAnyRaceGroupASeries = false; boolean hasAnyRaceGroupAFleet = false; + private final boolean ignoreLocalSettings; public void setEntryClickedHandler(EntryHandler handler) { this.entryClickedHandler = handler; @@ -137,7 +138,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component */ public RegattaRaceStatesComponent(final SailingServiceAsync sailingService, ErrorReporter errorReporter, final StringMessages stringMessages, final UUID eventId, RegattaRaceStatesSettings settings, - Timer timerToSynchronize) { + Timer timerToSynchronize, boolean ignoreLocalSettings) { this.sailingService = sailingService; this.stringMessages = stringMessages; this.eventId = eventId; @@ -148,6 +149,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component this.localStorageRegattaOverviewEventKey = LOCAL_STORAGE_REGATTA_OVERVIEW_KEY + eventId.toString(); this.flagInterpreter = new RaceStateFlagsInterpreter(stringMessages); this.settings = new RegattaRaceStatesSettings(); + this.ignoreLocalSettings = ignoreLocalSettings; loadAndSetSettings(settings); mainPanel = new VerticalPanel(); mainPanel.getElement().getStyle().setWidth(100, Unit.PCT); @@ -160,9 +162,11 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component } private void loadAndSetSettings(RegattaRaceStatesSettings settings) { - RegattaRaceStatesSettings loadedSettings = loadRegattaRaceStatesSettings(); - if (loadedSettings != null) { - settings = loadedSettings; + if(!ignoreLocalSettings) { + RegattaRaceStatesSettings loadedSettings = loadRegattaRaceStatesSettings(); + if (loadedSettings != null) { + settings = loadedSettings; + } } updateSettings(settings); } @@ -897,11 +901,8 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component return null; } private String createRegattaLink(RegattaOverviewEntryDTO entryDTO) { - Map leaderboardLinkParameters = new HashMap(); - leaderboardLinkParameters.put("name", entryDTO.regattaName); - leaderboardLinkParameters.put("showRaceDetails", String.valueOf(true)); - leaderboardLinkParameters.put("displayName", entryDTO.regattaDisplayName); - String leaderboardLink = EntryPointLinkFactory.createLeaderboardLink(leaderboardLinkParameters); + Map leaderboardLinkParameters = Collections.emptyMap(); + String leaderboardLink = EntryPointLinkFactory.createLeaderboardTabLink(eventId.toString(), entryDTO.regattaName, leaderboardLinkParameters); return leaderboardLink; } public void setRepeatedInfoLabel(FlowPanel repeatedInfoLabel) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java index 04dbbad2c58..0f1d8f5538c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java @@ -22,6 +22,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; +import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; @@ -37,8 +38,10 @@ import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; import com.sap.sailing.gwt.ui.shared.start.EventStageDTO; import com.sap.sailing.gwt.ui.shared.start.StageEventType; import com.sap.sailing.server.RacingEventService; +import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; +import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.media.ImageDescriptor; import com.sap.sse.common.media.MediaDescriptor; import com.sap.sse.common.media.MediaTagConstants; @@ -429,18 +432,20 @@ public final class HomeServiceUtil { return result; } - public static RegattaMetadataDTO toRegattaMetadataDTO(LeaderboardGroup leaderboardGroup, Leaderboard leaderboard) { + public static RegattaMetadataDTO toRegattaMetadataDTO(EventBase event, LeaderboardGroup leaderboardGroup, Leaderboard leaderboard) { RegattaMetadataDTO regattaDTO = new RegattaMetadataDTO(); - fillRegattaFields(leaderboardGroup, leaderboard, regattaDTO); + fillRegattaFields(event, leaderboardGroup, leaderboard, regattaDTO); return regattaDTO; } - public static void fillRegattaFields(LeaderboardGroup leaderboardGroup, Leaderboard leaderboard, + public static void fillRegattaFields(EventBase event, LeaderboardGroup leaderboardGroup, Leaderboard leaderboard, RegattaMetadataDTO regattaDTO) { regattaDTO.setId(leaderboard.getName()); regattaDTO.setDisplayName(leaderboard.getDisplayName() != null ? leaderboard.getDisplayName() : leaderboard.getName()); - regattaDTO.setBoatCategory(leaderboardGroup.getDisplayName() != null ? leaderboardGroup.getDisplayName() : leaderboardGroup.getName()); + if(hasMultipleLeaderboardGroups(event)) { + regattaDTO.setBoatCategory(leaderboardGroup.getDisplayName() != null ? leaderboardGroup.getDisplayName() : leaderboardGroup.getName()); + } regattaDTO.setCompetitorsCount(calculateCompetitorsCount(leaderboard)); regattaDTO.setRaceCount(calculateRaceCount(leaderboard)); regattaDTO.setTrackedRacesCount(calculateTrackedRaceCount(leaderboard)); @@ -451,16 +456,42 @@ public final class HomeServiceUtil { regattaDTO.setEndDate(regatta.getEndDate() != null ? regatta.getEndDate().asDate() : null); } regattaDTO.setState(calculateRegattaState(regattaDTO)); + regattaDTO.setDefaultCourseAreaName(getCourseAreaNameForRegattaIdThereIsMoreThanOne(event, leaderboard)); } + private static boolean hasMultipleLeaderboardGroups(EventBase event) { + return Util.size(event.getLeaderboardGroups()) > 1; + } + public static boolean hasLiveRace(LeaderboardDTO leaderboard) { List> liveRaces = leaderboard.getLiveRaces(getLiveTimePointInMillis()); return !liveRaces.isEmpty(); } + public static TimePoint getLiveTimePoint() { + return new MillisecondsTimePoint(getLiveTimePointInMillis()); + } + private static long getLiveTimePointInMillis() { // TODO better solution long livePlayDelayInMillis = 15_000; return System.currentTimeMillis() - livePlayDelayInMillis; } + + public static String getCourseAreaNameForRegattaIdThereIsMoreThanOne(EventBase event, Leaderboard leaderboard) { + /** The course area will not be shown if there is only one course area defined for the event */ + if (Util.size(event.getVenue().getCourseAreas()) <= 1) { + return null; + } + CourseArea courseArea = null; + if (leaderboard instanceof FlexibleLeaderboard) { + courseArea = ((FlexibleLeaderboard) leaderboard).getDefaultCourseArea(); + } else if(leaderboard instanceof RegattaLeaderboard) { + Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta(); + if (regatta != null) { + courseArea = regatta.getDefaultCourseArea(); + } + } + return courseArea == null ? null : courseArea.getName(); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml index c328c0935d3..52d0969c97e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/AdminConsole.gwt.xml @@ -9,7 +9,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetCoursePositionsAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetCoursePositionsAction.java deleted file mode 100644 index 0fb4ffb6cb4..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetCoursePositionsAction.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sap.sailing.gwt.ui.actions; - -import java.util.Date; - -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO; -import com.sap.sse.gwt.client.async.AsyncAction; - -public class GetCoursePositionsAction implements AsyncAction { - private final SailingServiceAsync sailingService; - private final RegattaAndRaceIdentifier raceIdentifier; - private final Date date; - - public GetCoursePositionsAction(SailingServiceAsync sailingService, RegattaAndRaceIdentifier raceIdentifier, Date date) { - this.sailingService = sailingService; - this.raceIdentifier = raceIdentifier; - this.date = date; - } - - @Override - public void execute(AsyncCallback callback) { - sailingService.getCoursePositions(raceIdentifier, date, callback); - } -} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java index 7140acfc830..288e06bbe1b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsoleEntryPoint.java @@ -33,6 +33,7 @@ import com.sap.sse.gwt.client.EntryPointHelper; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; import com.sap.sse.gwt.client.controls.filestorage.FileStoragePanel; +import com.sap.sse.gwt.resources.Highcharts; import com.sap.sse.security.ui.client.component.UserManagementPanel; import com.sap.sse.security.ui.client.i18n.StringMessages; @@ -45,6 +46,7 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint implements @Override protected void doOnModuleLoad() { + Highcharts.ensureInjectedWithMore(); super.doOnModuleLoad(); EntryPointHelper.registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath); createUI(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java index 5b44f253063..a86275481e8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventDetailsComposite.java @@ -157,7 +157,7 @@ public class EventDetailsComposite extends Composite { } videoURLList.setValues(videoURLStringsAsList); List leaderboardGroupNamesAsList = new ArrayList<>(); - for(LeaderboardGroupDTO leaderboardGroupDTO: event.getLeaderboardGroups()) { + for (LeaderboardGroupDTO leaderboardGroupDTO : event.getLeaderboardGroups()) { leaderboardGroupNamesAsList.add(leaderboardGroupDTO.getName()); } leaderboardGroupList.setValues(leaderboardGroupNamesAsList); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/EntryPointLinkFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/EntryPointLinkFactory.java index 7c5ba55e83b..66840bcf631 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/EntryPointLinkFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/EntryPointLinkFactory.java @@ -17,6 +17,10 @@ public class EntryPointLinkFactory extends AbstractEntryPointLinkFactory { return createEntryPointLink("/gwt/Leaderboard.html", parameters); } + public static String createLeaderboardTabLink(String eventId, String regattaId, Map parameters) { + return createEntryPointLink("/gwt/Home.html#/regatta/leaderboard/:eventId=" + eventId + "®attaId=" + regattaId, parameters); + } + public static String createLeaderboardGroupLink(Map parameters) { return createEntryPointLink("/gwt/Spectator.html", parameters); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RequiresDataInitialization.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RequiresDataInitialization.java index cf3f1216d15..c6134d49cde 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RequiresDataInitialization.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/RequiresDataInitialization.java @@ -9,7 +9,7 @@ package com.sap.sailing.gwt.ui.client; */ public interface RequiresDataInitialization { - public void initializeData(boolean showMapControls); + public void initializeData(boolean showMapControls, boolean showHeaderPanel); public boolean isDataInitialized(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index f208910de19..4b751160c6c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -614,4 +614,6 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS ArrayList getLeaderboardGroupsByEventId(UUID id); boolean doesRegattaLogContainCompetitors(String name) throws DoesNotHaveRegattaLogException; + + RegattaAndRaceIdentifier getRaceIdentifier(String regattaLikeName, String raceColumnName, String fleetName); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index f9be82f07e1..1b6a87df0f0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -719,4 +719,6 @@ public interface SailingServiceAsync extends BuildVersionRetriever, FileStorageM void getLeaderboardGroupsByEventId(UUID id, AsyncCallback> callback); void doesRegattaLogContainCompetitors(String name, AsyncCallback regattaLogCallBack); + + void getRaceIdentifier(String regattaLikeName, String raceColumnName, String fleetName, AsyncCallback asyncCallback); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java old mode 100755 new mode 100644 index e40d1065dc8..c2e2388b331 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -499,6 +499,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String scoringSchemeHighPointFirstGetsTen(); String knotsUnit(); String knotsValue(double value); + String knotsRange(double lowerValue, double upperValue); String averageJibeLossInMetersTooltip(); String averageManeuverLossInMetersTooltip(); String averageTackLossInMetersTooltip(); @@ -1275,6 +1276,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String competitorsCount(@PluralCount(DefaultRule_1_0n.class) int count); String trackedRacesCount(@PluralCount(DefaultRule_1_0n.class) int count); String eventsCount(@PluralCount(DefaultRule_1_0n.class) int count); + String courseAreaName(String courseAreaName); String currentOfTotalLegs(int currentLeg, int totalLegs); String clone(); String changed(); @@ -1333,12 +1335,19 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String noNews(); String pointsValue(double value); String openLeaderboard(); - String resultsStandings(); String now(); String minutesAgo(String minutes); String hoursAgo(String hours); + String winner(); + String durationPlain(); + String finishedRaces(); String selectCompetitor(); + String allBoatClasses(); String points(); String numberOfRacesShort(); + String courseGeometry(); + String degreesToWind(String diff); String miniLeeaderboardLegendText(); + String currentOfTotal(int current, int total); + String noFinishedRaces(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties old mode 100755 new mode 100644 index 31757f394dc..5de14eb8538 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -521,6 +521,7 @@ raceIsLive=Race {0} is live racesAreLive=Races {0} are live knotsUnit=kts knotsValue={0,number,#0.0} kts +knotsRange={0,number,#0.0} - {1,number,#0.0} kts scoringSchemeHighPointFirstGetsOne=High Point, winner gets 1 point scoringSchemeHighPointFirstGetsTen=High Point, winner gets 10 points knotsUnit=kts @@ -881,7 +882,7 @@ errorRunningDataMiningQuery=An error occured running the query hideToolbar=Hide toolbar showSeriesLeaderboards=Show series leaderboards showOverallLeaderboard=Show overall leaderboard -lineAngleToWindAndAdvantage=length: {0}m; angle to wind: {1}deg; {2} side has {3}m advantage +lineAngleToWindAndAdvantage=length: {0}m\nangle to wind: {1}deg\n{2} side has {3}m advantage doYouReallyWantToRemoveEvents=Do you really want to remove the events? doYouReallyWantToRemoveEvent=Do you really want to remove event {0}? line=Line @@ -1241,6 +1242,7 @@ trackedRacesCount[one]={0,number} tracked race eventsCount={0,number} events eventsCount[one]={0,number} event competitorsCount={0,number} competitors +courseAreaName=Course area: {0} competitorsCount[one]={0,number} competitor currentOfTotalLegs=Leg {0,number}/{1,number} clone=Clone @@ -1302,11 +1304,18 @@ noResults=There are no results available yet. noNews=There are no news available. pointsValue={0,number,#0.00} openLeaderboard=Open Leaderboard -resultsStandings=Results/Standings now=Now minutesAgo={0} min ago hoursAgo={0} h ago +winner=Winner +durationPlain=Duration +finishedRaces=Finished Races selectCompetitor=Please select one competitor +allBoatClasses=All Boat Classes points=Points numberOfRacesShort=# Races -miniLeeaderboardLegendText=Shows the points along with the number of races per competitor \ No newline at end of file +courseGeometry=Course Geometry +degreesToWind={0}° to wind +miniLeeaderboardLegendText=Shows the points along with the number of races per competitor +currentOfTotal={0,number}/{1,number} +noFinishedRaces=There are no finished races yet diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties old mode 100755 new mode 100644 index a2eb4896fbb..9c268eebaa0 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -524,6 +524,7 @@ scoringSchemeHighPointFirstGetsOne=High Point System, Gewinner erhält 1 Punkt scoringSchemeHighPointFirstGetsTen=High Point System, Gewinner erhält 10 Punkte knotsUnit=kn knotsValue={0,number,#0.0} kn +knotsRange={0,number,#0.0} - {1,number,#0.0} kn generatePolarSheet=Polardiagramm erstellen polars=Polardiagramme polarSheetChart=Polardiagramm @@ -877,7 +878,7 @@ errorRunningDataMiningQuery=Es ist ein Felher bei der Datenabfrage aufgetreten hideToolbar=Toolbar ausblenden showSeriesLeaderboards=Ergebnisse der Serie anzeigen showOverallLeaderboard=Gesamtergebnisse anzeigen -lineAngleToWindAndAdvantage=Länge: {0}m; Winkel zum Wind: {1}°; {2}-Seite ist {3}m bevorteilt +lineAngleToWindAndAdvantage=Länge: {0}m\nWinkel zum Wind: {1}°\n{2}-Seite ist {3}m bevorteilt doYouReallyWantToRemoveEvents=Möchten Sie diese Veranstaltungen wirklich entfernen? doYouReallyWantToRemoveEvent=Möchten Sie wirklich die Veranstaltung {0} entfernen? line=Linie @@ -1231,6 +1232,7 @@ trackedRacesCount[one]={0,number} getracktes Rennen eventsCount={0,number} Veranstaltungen eventsCount[one]={0,number} Veranstaltung competitorsCount={0,number} Teams +courseAreaName=Regattabahn: {0} competitorsCount[one]={0,number} Team currentOfTotalLegs=Schenkel {0,number}/{1,number} clone=Clone @@ -1273,7 +1275,7 @@ raceCalcluatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Ber.Zeit@ raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Berechnete Zeit bei erwarteter Ankunft am derzeit absolut in Führung liegenden Teilnehmers;\nSchätzung basierend auf durchschnittlichem VMG im aktuellen Bahnschenkel und gleicher verrechneter Geschwindigket wie der absolut in Führung liegende Teilnehmer\nfür alle nachfolgenden Bahnschenkel sailorInfo=Segler Infos sailorInfoLongText=Offizielle Ergebnisse, Proteste,\nNotice Board, Segler-Wetter: -resultsQuickfinder=Ergebnis Schnellauswahl +resultsQuickfinder=Ergebnis-Schnellauswahl statistics=Statistiken impressions=Eindrücke notAvailableOnMobileMessage=Diese Ansicht ist noch nicht für die Mobile bzw. Smartphone Ansicht umgesetzt.\nWollen Sie die Desktop Ansicht besuchen? @@ -1292,11 +1294,18 @@ noResults=Leider gibt es noch keine Ergebnisse. noNews=Leider gibt es keine Neuigkeiten. pointsValue={0,number,#0.00} openLeaderboard=Rangliste öffnen -resultsStandings=Ergbnisse/Platzierungen now=Jetzt minutesAgo=vor {0} min hoursAgo=vor {0} h +winner=Sieger +durationPlain=Dauer +finishedRaces=Beendete Wettfahrten selectCompetitor=Bitte einen Teilnehmer auswählen +allBoatClasses=Alle Bootsklassen points=Punkte numberOfRacesShort=# Rennen -miniLeeaderboardLegendText=Es werden die Punkte und die Anzahl Rennen je Team angezeigt \ No newline at end of file +courseGeometry=Kursgeometrie +degreesToWind={0}° zum Wind +miniLeeaderboardLegendText=Es werden die Punkte und die Anzahl Rennen je Team angezeigt +currentOfTotal={0,number}/{1,number} +noFinishedRaces=Es wurden noch keine Wettfahrten beendet. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties old mode 100755 new mode 100644 index 81fee10254f..fd594e89956 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -510,6 +510,7 @@ raceIsLive=Гонка {0} в прямом эфире racesAreLive=Гонки {0} в прямом эфире knotsUnit=Узлы knotsValue={0,number,#0.0} Узлы +knotsRange={0,number,#0.0} - {1,number,#0.0} Узлы scoringSchemeHighPointFirstGetsOne=Самый лучший результат, победитель получает 1 очко scoringSchemeHighPointFirstGetsTen=Самый лучший результат, победитель получает 10 очков generatePolarSheet=Создать полярную электронную таблицу @@ -870,7 +871,7 @@ errorRunningDataMiningQuery=Ошибка при выполнении запро hideToolbar=Скрыть панель инструментов showSeriesLeaderboards=Показать таблицу лидеров серии showOverallLeaderboard=Показать общую таблицу лидеров -lineAngleToWindAndAdvantage=длина: {0}м; угол к ветру: {1}градус; {2} борт {3}м имеет преимущество +lineAngleToWindAndAdvantage=длина: {0}м\nугол к ветру: {1}градус\n{2} борт {3}м имеет преимущество doYouReallyWantToRemoveEvents=Вы действительно хотите удалить все события? doYouReallyWantToRemoveEvent=Вы действительно хотите удалить событие {0}? line=Строка @@ -1179,6 +1180,7 @@ trackedRacesCount[one]={0,number} tracked race eventsCount={0,number} events eventsCount[one]={0,number} event competitorsCount={0,number} competitors +courseAreaName=Зона курса: {0} competitorsCount[one]={0,number} competitor currentOfTotalLegs=Этап {0,number}/{1,number} clone=Clone @@ -1239,11 +1241,18 @@ noResults=There are no results available yet. noNews=There are no news available. pointsValue={0,number,#0.00} openLeaderboard=Open Leaderboard -resultsStandings=Results/Standings now=в настоящее время minutesAgo={0} минут назад hoursAgo={0} h часов назад +winner=победитель +durationPlain=Продолжительность +finishedRaces=Finished Races selectCompetitor=Please select one competitor +allBoatClasses=All Boat Classes points=Points numberOfRacesShort=# Races -miniLeeaderboardLegendText=Shows points along with the number of races per competitor \ No newline at end of file +courseGeometry=Course Geometry +degreesToWind={0}° to wind +miniLeeaderboardLegendText=Shows points along with the number of races per competitor +currentOfTotal={0,number}/{1,number} +noFinishedRaces=There are no finished races yet diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties index 6a31bfae1a3..b076fdb8bc0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties @@ -391,7 +391,7 @@ listRaces=List Races dropReplicaConnection=Drop connection errorTryingToObtainOverallLeaderboards=Error trying to obtain overall leaderboards: {0} distanceTraveled=行驶距离 -lineAngleToWindAndAdvantage=length: {0}m; angle to wind: {1}deg; {2} side has {3}m advantage +lineAngleToWindAndAdvantage=length: {0}m\nangle to wind: {1}deg\n{2} side has {3}m advantage mapDevices=Map Devices to Competitors and Marks scoringSchemeHighPointFirstGetsTenOrEight=High Point, winner gets 10 points (or 8) playModeLive=Live @@ -978,6 +978,7 @@ competitor=参赛队 tracked=跟踪 knotsUnit=kts knotsValue={0,number,#0.0} kts +knotsRange={0,number,#0.0} - {1,number,#0.0} kts errorTryingToObtainRaceLogMarkPassingData=Error trying to obtain race log mark passing data: {0} imageURLs=Image URLs speedOverGroundAtRaceStartTooltip=起航时的实际速度 @@ -1229,6 +1230,7 @@ trackedRacesCount[one]={0,number} tracked race eventsCount={0,number} events eventsCount[one]={0,number} event competitorsCount={0,number} competitors +courseAreaName=Course area: {0} competitorsCount[one]={0,number} competitor� dashboardWindBotLoadingText=Loading wind bot data ... dashboardWindBotLoadingMessage=Depending on your connection this operation might take some minutes to complete the first time. @@ -1273,6 +1275,9 @@ raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Calc.Time raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Calculated time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (calculated) performance to fastest boat in all subsequent legs sailorInfo=水手信息 sailorInfoLongText=Find here Notice Board,\nProtests, Sailor weather: +winner=Winner +durationPlain=Duration +finishedRaces=Finished Races resultsQuickfinder=Results Quickfinder statistics=Statistics impressions=Impressions @@ -1291,8 +1296,12 @@ noResults=There are no results available yet. noNews=There are no news available. pointsValue={0,number,#0.00} openLeaderboard=Open Leaderboard -resultsStandings=Results/Standings selectCompetitor=Please select one competitor +allBoatClasses=All Boat Classes points=Points numberOfRacesShort=# Races -miniLeeaderboardLegendText=Shows points along with the number of races per competitor \ No newline at end of file +courseGeometry=Course Geometry +degreesToWind={0}° to wind +miniLeeaderboardLegendText=Shows points along with the number of races per competitor +currentOfTotal={0,number}/{1,number} +noFinishedRaces=There are no finished races yet diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java index 8bd1ddc7bb7..c75bcbe4c84 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/TimePanel.java @@ -20,6 +20,9 @@ import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.TimePanelCssResources.TimePanelCss; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; +import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.gwt.client.controls.slider.SliderBar; import com.sap.sse.gwt.client.controls.slider.TimeSlider; import com.sap.sse.gwt.client.player.PlayStateListener; @@ -331,9 +334,13 @@ public class TimePanel extends SimplePanel implemen if (timeRangeProvider.isZoomed()) { timeSlider.setCurrentValue(new Double(newTime.getTime()), false); } else { + if (getFromTime() == null || getToTime() == null) { + initializeTimeRange(oldTime, newTime); + } if (getFromTime() != null && getToTime() != null) { // handle the case where time advances beyond slider's end. if (newTime.after(getToTime())) { + // in live mode, expand the range switch (timer.getPlayMode()) { case Live: Date newMaxTime = new Date(newTime.getTime()); @@ -343,6 +350,7 @@ public class TimePanel extends SimplePanel implemen setMinMax(getFromTime(), newMaxTime, /* fireEvent */ false); // no event because we guarantee that time is between min/max break; case Replay: + // in replay, stop playing when the end of the range has been reached timer.pause(); break; } @@ -362,6 +370,22 @@ public class TimePanel extends SimplePanel implemen } } + /** + * When the time range is not yet properly initialized and we get a time change, adjust the time range so that old and + * new time are in range. A margin of one minute is used before and after. This method assumes that at least one of the + * two time points passed as parameters has a value other than null. Should the other be null, + * it is ignored. No assumptions are made about the temporal relation of the two time points to each other; in other + * words, oldTime may also be after newTime which could, e.g., be the case for a timer running + * backwards. + */ + private void initializeTimeRange(Date oldTime, Date newTime) { + TimePoint from = Util.getEarliestOfTimePoints(getFromTime()==null?null:new MillisecondsTimePoint(getFromTime()), oldTime==null?null:new MillisecondsTimePoint(oldTime)); + from = Util.getEarliestOfTimePoints(from, newTime==null?null:new MillisecondsTimePoint(newTime)); + TimePoint to = Util.getEarliestOfTimePoints(getToTime()==null?null:new MillisecondsTimePoint(getToTime()), oldTime==null?null:new MillisecondsTimePoint(oldTime)); + from = Util.getEarliestOfTimePoints(to, newTime==null?null:new MillisecondsTimePoint(newTime)); + timeRangeProvider.setTimeRange(from==null?null:from.asDate(), to==null?null:to.asDate()); + } + protected String getTimeToStartLabelText(Date time) { return null; } @@ -595,4 +619,8 @@ public class TimePanel extends SimplePanel implemen public String getDependentCssClassName() { return "timePanel"; } + + public Button getBackToLiveButton() { + return backToLivePlayButton; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java index a0e443d95a6..c98c5e1c16f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/WindChart.java @@ -44,6 +44,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.impl.ColorMapImpl; import com.sap.sailing.gwt.ui.actions.GetWindInfoAction; +import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.RaceSelectionProvider; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; @@ -86,7 +87,9 @@ public class WindChart extends AbstractRaceChart implements Componentnull, this chart won't update its contents automatically upon race selection change; * otherwise, whenever the selection changes, the wind data of the race selected now is loaded from the - * server and displayed in this chart. If no race is selected, the chart is cleared. + * server and displayed in this chart. If no race is selected, the chart is cleared. The caller of this + * constructor must ensure to trigger {@link RaceSelectionChangeListener#onRaceSelectionChange(List)} at + * least once to ensure that this chart sets its {@link AbstractRaceChart#selectedRaceIdentifier} field. */ public WindChart(SailingServiceAsync sailingService, RaceSelectionProvider raceSelectionProvider, Timer timer, TimeRangeWithZoomProvider timeRangeWithZoomProvider, WindChartSettings settings, final StringMessages stringMessages, @@ -518,38 +521,35 @@ public class WindChart extends AbstractRaceChart implements Component timeOfLatestRequestInMillis) { - loadData(new Date(timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(), /* append */true); - } - // otherwise the cache spans across date and so we don't need to load anything - break; - } - case Replay: - { - if (timeOfLatestRequestInMillis == null) { - // pure replay mode - loadData(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), /* append */false); - } else { - // replay mode during live play + if (isVisible()) { + updateTimePlotLine(newTime); + switch (timer.getPlayMode()) { + case Live: + { + // is date before first cache entry or is cache empty? if (timeOfEarliestRequestInMillis == null || newTime.getTime() < timeOfEarliestRequestInMillis) { loadData(timeRangeWithZoomProvider.getFromTime(), newTime, /* append */ true); } else if (newTime.getTime() > timeOfLatestRequestInMillis) { - loadData(new Date(timeOfLatestRequestInMillis), newTime, /* append */true); - } + loadData(new Date(timeOfLatestRequestInMillis), timeRangeWithZoomProvider.getToTime(), /* append */true); + } + // otherwise the cache spans across date and so we don't need to load anything + break; + } + case Replay: + { + if (timeOfLatestRequestInMillis == null) { + // pure replay mode + loadData(timeRangeWithZoomProvider.getFromTime(), timeRangeWithZoomProvider.getToTime(), /* append */false); + } else { + // replay mode during live play + if (timeOfEarliestRequestInMillis == null || newTime.getTime() < timeOfEarliestRequestInMillis) { + loadData(timeRangeWithZoomProvider.getFromTime(), newTime, /* append */ true); + } else if (newTime.getTime() > timeOfLatestRequestInMillis) { + loadData(new Date(timeOfLatestRequestInMillis), newTime, /* append */true); + } + } + break; } - break; } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 1dd44ddb30b..6b5d10c274d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -100,16 +100,19 @@ import com.sap.sailing.gwt.ui.client.shared.filter.QuickRankProvider; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.HelpLineTypes; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; +import com.sap.sailing.gwt.ui.shared.ControlPointDTO; import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO; 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.QuickRankDTO; +import com.sap.sailing.gwt.ui.shared.RaceCourseDTO; import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; import com.sap.sailing.gwt.ui.shared.SidelineDTO; import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO; +import com.sap.sailing.gwt.ui.shared.WaypointDTO; import com.sap.sailing.gwt.ui.shared.WindDTO; import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO; import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; @@ -118,6 +121,7 @@ import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapStyleHelper; import com.sap.sailing.gwt.ui.shared.racemap.RaceSimulationOverlay; import com.sap.sailing.gwt.ui.shared.racemap.WindStreamletsRaceboardOverlay; import com.sap.sse.common.Util; +import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Triple; import com.sap.sse.common.filter.Filter; import com.sap.sse.common.filter.FilterSet; @@ -209,11 +213,24 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe private AdvantageLineMouseOverMapHandler advantageLineMouseOverHandler; /** - * Polyline for the course middle line. + * Polylines for the course middle lines; keys are the two control points delimiting the leg for which the + * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there + * are multiple legs using the same control points in different directions, using a {@link Set} makes this + * independent of the order of the two control points. If no course middle line is currently being shown for + * a pair of control points, the map will not contain a value for this pair. */ - private Polyline courseMiddleLine; + private final Map, Polyline> courseMiddleLines; - private Map courseSidelines; + private final Map courseSidelines; + + /** + * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the + * lines that show the tooltip text in a little info box linked to the line. When the line is removed by + * {@link #showOrRemoveOrUpdateLine(Polyline, boolean, Position, Position, LineInfoProvider, String)}, these + * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is + * deactivated, all these overlays need to go. + */ + private final Map infoOverlaysForLinesForCourseGeometry; /** * Wind data used to display the advantage line. Retrieved by a {@link GetWindInfoAction} execution and used in @@ -382,7 +399,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe public RaceMap(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, CompetitorSelectionProvider competitorSelection, StringMessages stringMessages, boolean showMapControls, boolean showViewStreamlets, boolean showViewSimulation, - RegattaAndRaceIdentifier raceIdentifier, CombinedWindPanelStyle combinedWindPanelStyle) { + RegattaAndRaceIdentifier raceIdentifier, CombinedWindPanelStyle combinedWindPanelStyle, boolean showHeaderPanel) { this.setSize("100%", "100%"); this.showMapControls = showMapControls; this.stringMessages = stringMessages; @@ -394,7 +411,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe timer.addTimeListener(this); raceMapImageManager = new RaceMapImageManager(); markDTOs = new HashMap(); - courseSidelines = new HashMap(); + courseSidelines = new HashMap<>(); + courseMiddleLines = new HashMap<>(); + infoOverlaysForLinesForCourseGeometry = new HashMap<>(); boatOverlays = new HashMap(); competitorInfoOverlays = new HashMap(); windSensorOverlays = new HashMap(); @@ -414,7 +433,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe headerPanel.setStyleName("RaceMap-HeaderPanel"); panelForLeftHeaderLabels = new AbsolutePanel(); panelForRightHeaderLabels = new AbsolutePanel(); - initializeData(showMapControls); + initializeData(showMapControls, showHeaderPanel); combinedWindPanel = new CombinedWindPanel(raceMapImageManager, combinedWindPanelStyle, stringMessages, coordinateSystem); combinedWindPanel.setVisible(false); trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, combinedWindPanelStyle, stringMessages, coordinateSystem); @@ -483,8 +502,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe public void execute() { if (map != null) { map.setOptions(mapOptions); - final LatLngBounds newBounds = getDefaultZoomBounds(); - zoomMapToNewBounds(newBounds); + // ensure zooming to what the settings tell, or defaults if what the settings tell isn't possible right now + mapFirstZoomDone = false; trueNorthIndicatorPanel.redraw(); orientationChangeInProgress = false; } @@ -492,7 +511,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe }); } - private void loadMapsAPIV3(final boolean showMapControls) { + private void loadMapsAPIV3(final boolean showMapControls, final boolean showHeaderPanel) { boolean sensor = true; // load all the libs for use in the maps @@ -506,8 +525,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); map = new MapWidget(mapOptions); RaceMap.this.add(map, 0, 0); - Image sapLogo = createSAPLogo(); - RaceMap.this.add(sapLogo); + if (showHeaderPanel) { + Image sapLogo = createSAPLogo(); + RaceMap.this.add(sapLogo); + } map.setControls(ControlPosition.LEFT_TOP, combinedWindPanel); combinedWindPanel.getParent().addStyleName("CombinedWindPanelParentDiv"); map.setControls(ControlPosition.LEFT_TOP, trueNorthIndicatorPanel); @@ -592,19 +613,26 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe simulationOverlay.addToMap(); simulationOverlay.setVisible(false); } - - createHeaderPanel(map); + if (showHeaderPanel) { + createHeaderPanel(map); + } createSettingsButton(map); // Data has been initialized RaceMap.this.isMapInitialized = true; RaceMap.this.redraw(); trueNorthIndicatorPanel.redraw(); + showAdditionalControls(map); } }; - LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey); } + + /** + * Subclasses may define additional stuff to be shown on the map. + */ + protected void showAdditionalControls(MapWidget map) { + } private void setHasPolar() { GetPolarAction getPolar = new GetPolarAction(sailingService, raceIdentifier); @@ -753,7 +781,6 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe getRaceMapDataCallback(oldTime, newTime, fromAndToAndOverlap.getC(), competitorsToShow, requestID)); // draw the wind into the map, get the combined wind - // TODO bug2057 also fetch wind for LEG_MIDDLE for all legs because this needs to be the basis for the advantage line display List windSourceTypeNames = new ArrayList(); windSourceTypeNames.add(WindSourceType.EXPEDITION.name()); windSourceTypeNames.add(WindSourceType.COMBINED.name()); @@ -879,7 +906,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe updateCoordinateSystemFromSettings(); } showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); - showStartAndFinishLines(raceMapDataDTO.coursePositions); + showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to // adjust to new boat positions @@ -1297,254 +1324,268 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe private final StringBuilder leewardStartLineMarkToFirstMarkLineText = new StringBuilder(); private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){ - if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE) - && courseDTO.startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) { - LatLng windwardStartLinePoint = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(0)); - LatLng leewardStartLinePoint = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(1)); - LatLng firstMarkPoint = coordinateSystem.toLatLng(courseDTO.waypointPositions.get(1)); - windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(), - stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(courseDTO.startMarkPositions.get(0).getDistance(courseDTO.waypointPositions.get(1)) - .getMeters()))); - leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(), - stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(courseDTO.startMarkPositions.get(1).getDistance(courseDTO.waypointPositions.get(1)) - .getMeters()))); - if (windwardStartLineMarkToFirstMarkLine == null && leewardStartLineMarkToFirstMarkLine == null) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setGeodesic(true); - options.setStrokeColor("grey"); - options.setStrokeWeight(1); - options.setStrokeOpacity(1.0); - - MVCArray windwardLinePoints = MVCArray.newInstance(); - windwardLinePoints.insertAt(0, windwardStartLinePoint); - windwardLinePoints.insertAt(1, firstMarkPoint); - - MVCArray leewardLinePoints = MVCArray.newInstance(); - leewardLinePoints.insertAt(0, leewardStartLinePoint); - leewardLinePoints.insertAt(1, firstMarkPoint); - - windwardStartLineMarkToFirstMarkLine = Polyline.newInstance(options); - windwardStartLineMarkToFirstMarkLine.setPath(windwardLinePoints); - windwardStartLineMarkToFirstMarkLine.addMouseOverHandler(new MouseOverMapHandler() { - @Override - public void onEvent(MouseOverMapEvent event) { - map.setTitle(windwardStartLineMarkToFirstMarkLineText.toString()); - } - }); - windwardStartLineMarkToFirstMarkLine.addMouseOutMoveHandler(new MouseOutMapHandler() { - @Override - public void onEvent(MouseOutMapEvent event) { - map.setTitle(""); - } - }); - leewardStartLineMarkToFirstMarkLine = Polyline.newInstance(options); - leewardStartLineMarkToFirstMarkLine.setPath(leewardLinePoints); - leewardStartLineMarkToFirstMarkLine.addMouseOverHandler(new MouseOverMapHandler() { - @Override - public void onEvent(MouseOverMapEvent event) { - map.setTitle(leewardStartLineMarkToFirstMarkLineText.toString()); - } - }); - leewardStartLineMarkToFirstMarkLine.addMouseOutMoveHandler(new MouseOutMapHandler() { - @Override - public void onEvent(MouseOutMapEvent event) { - map.setTitle(""); - } - }); - - windwardStartLineMarkToFirstMarkLine.setMap(map); - leewardStartLineMarkToFirstMarkLine.setMap(map); - - } else { - windwardStartLineMarkToFirstMarkLine.getPath().removeAt(1); - windwardStartLineMarkToFirstMarkLine.getPath().removeAt(0); - windwardStartLineMarkToFirstMarkLine.getPath().insertAt(0, windwardStartLinePoint); - windwardStartLineMarkToFirstMarkLine.getPath().insertAt(1, firstMarkPoint); - - leewardStartLineMarkToFirstMarkLine.getPath().removeAt(1); - leewardStartLineMarkToFirstMarkLine.getPath().removeAt(0); - leewardStartLineMarkToFirstMarkLine.getPath().insertAt(0, leewardStartLinePoint); - leewardStartLineMarkToFirstMarkLine.getPath().insertAt(1, firstMarkPoint); + final List startMarkPositions = courseDTO.getStartMarkPositions(); + final Position windwardStartLinePosition = startMarkPositions.get(0); + final Position leewardStartLinePosition = startMarkPositions.get(1); + final Position firstMarkPosition = courseDTO.waypointPositions.get(1); + windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(), + stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal + .format(windwardStartLinePosition.getDistance(firstMarkPosition) + .getMeters()))); + leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(), + stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal + .format(leewardStartLinePosition.getDistance(firstMarkPosition) + .getMeters()))); + final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return windwardStartLineMarkToFirstMarkLineText.toString(); } - } else { - if (windwardStartLineMarkToFirstMarkLine != null) { - windwardStartLineMarkToFirstMarkLine.setMap(null); - windwardStartLineMarkToFirstMarkLine = null; + }; + final LineInfoProvider leewardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return leewardStartLineMarkToFirstMarkLineText.toString(); } - if (leewardStartLineMarkToFirstMarkLine != null) { - leewardStartLineMarkToFirstMarkLine.setMap(null); - leewardStartLineMarkToFirstMarkLine = null; - } - } + }; + windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */ + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) + && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, + windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, + "grey"); + leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) + && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, + leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, + "grey"); } private final StringBuilder startLineAdvantageText = new StringBuilder(); private final StringBuilder finishLineAdvantageText = new StringBuilder(); + final LineInfoProvider startLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.startLine()+startLineAdvantageText; + } + }; + final LineInfoProvider finishLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.finishLine()+finishLineAdvantageText; + } + }; - private void showStartAndFinishLines(final CoursePositionsDTO courseDTO) { - if (map != null && courseDTO != null && lastRaceTimesInfo != null) { + private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) { + if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null && + !courseDTO.course.waypoints.isEmpty()) { com.sap.sse.common.Util.Pair leadingVisibleCompetitorInfo = getFarthestAheadVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow()); - int legOfLeadingCompetitor = leadingVisibleCompetitorInfo == null ? -1 : leadingVisibleCompetitorInfo.getA(); - int numberOfLegs = lastRaceTimesInfo.legInfos.size(); + int oneBasedLegOfLeadingCompetitor = leadingVisibleCompetitorInfo == null ? -1 : leadingVisibleCompetitorInfo.getA(); + int numberOfLegs = courseDTO.course.waypoints.size()-1; // draw the start line - updateCountdownCanvas(courseDTO.startMarkPositions); - if (legOfLeadingCompetitor <= 1 && - settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && courseDTO.startMarkPositions != null && courseDTO.startMarkPositions.size() == 2) { - LatLng startLinePoint1 = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(0)); - LatLng startLinePoint2 = coordinateSystem.toLatLng(courseDTO.startMarkPositions.get(1)); - if (courseDTO.startLineAngleToCombinedWind != null) { - startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( - NumberFormat.getFormat("0.0").format(courseDTO.startLineLengthInMeters), - NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.startLineAngleToCombinedWind)), - courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), - NumberFormat.getFormat("0.0").format(courseDTO.startLineAdvantageInMeters))); - } else { - startLineAdvantageText.delete(0, startLineAdvantageText.length()); - } - if (startLine == null) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setClickable(true); - options.setGeodesic(true); - options.setStrokeColor("#FFFFFF"); - options.setStrokeWeight(1); - options.setStrokeOpacity(1.0); - - MVCArray pointsAsArray = MVCArray.newInstance(); - pointsAsArray.insertAt(0, startLinePoint1); - pointsAsArray.insertAt(1, startLinePoint2); - - startLine = Polyline.newInstance(options); - startLine.setPath(pointsAsArray); - - startLine.addMouseOverHandler(new MouseOverMapHandler() { - @Override - public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.startLine()+startLineAdvantageText); - } - }); - startLine.addMouseOutMoveHandler(new MouseOutMapHandler() { - @Override - public void onEvent(MouseOutMapEvent event) { - map.setTitle(""); - } - }); - startLine.setMap(map); - } else { - startLine.getPath().removeAt(1); - startLine.getPath().removeAt(0); - startLine.getPath().insertAt(0, startLinePoint1); - startLine.getPath().insertAt(1, startLinePoint2); - } + final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0); + updateCountdownCanvas(startWaypoint); + final Position startLineLeftPosition = courseDTO.getStartMarkPositions().get(0); + final Position startLineRightPosition = courseDTO.getStartMarkPositions().get(1); + if (courseDTO.startLineAngleToCombinedWind != null) { + startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( + NumberFormat.getFormat("0.0").format(courseDTO.startLineLengthInMeters), + NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.startLineAngleToCombinedWind)), + courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), + NumberFormat.getFormat("0.0").format(courseDTO.startLineAdvantageInMeters))); } else { - if (startLine != null) { - startLine.setMap(null); - startLine = null; - } + startLineAdvantageText.delete(0, startLineAdvantageText.length()); } + startLine = showOrRemoveOrUpdateLine(startLine, /* showLine */ courseDTO.getStartMarkPositions() != null && + courseDTO.getStartMarkPositions().size() == 2 && + ((oneBasedLegOfLeadingCompetitor <= 1 && + settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) || + settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)), + startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff"); + // TODO bug3027: show SmallTransparentInfoOverlay in addition to tooltip if course geometry option is true; remove it if option is false + // TODO bug3027: show *all* course middle lines if course geometry option is true + // draw the finish line - if (legOfLeadingCompetitor > 0 && legOfLeadingCompetitor == numberOfLegs && - settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && courseDTO.finishMarkPositions != null && courseDTO.finishMarkPositions.size() == 2) { - LatLng finishLinePoint1 = coordinateSystem.toLatLng(courseDTO.finishMarkPositions.get(0)); - LatLng finishLinePoint2 = coordinateSystem.toLatLng(courseDTO.finishMarkPositions.get(1)); - if (courseDTO.startLineAngleToCombinedWind != null) { - finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( - NumberFormat.getFormat("0.0").format(courseDTO.finishLineLengthInMeters), - NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.finishLineAngleToCombinedWind)), - courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), - NumberFormat.getFormat("0.0").format(courseDTO.finishLineAdvantageInMeters))); - } else { - finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); - } - if (finishLine == null) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setClickable(true); - options.setGeodesic(true); - options.setStrokeColor("#000000"); - options.setStrokeWeight(1); - options.setStrokeOpacity(1.0); - - MVCArray pointsAsArray = MVCArray.newInstance(); - pointsAsArray.insertAt(0, finishLinePoint1); - pointsAsArray.insertAt(1, finishLinePoint2); - - finishLine = Polyline.newInstance(options); - finishLine.setPath(pointsAsArray); - - finishLine.addMouseOverHandler(new MouseOverMapHandler() { - @Override - public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.finishLine()+finishLineAdvantageText); - } - }); - finishLine.addMouseOutMoveHandler(new MouseOutMapHandler() { - @Override - public void onEvent(MouseOutMapEvent event) { - map.setTitle(""); - } - }); - finishLine.setMap(map); - } else { - finishLine.getPath().removeAt(1); - finishLine.getPath().removeAt(0); - finishLine.getPath().insertAt(0, finishLinePoint1); - finishLine.getPath().insertAt(1, finishLinePoint2); - } + final Position finishLineLeftPosition = courseDTO.getFinishMarkPositions().get(0); + final Position finishLineRightPosition = courseDTO.getFinishMarkPositions().get(1); + if (courseDTO.startLineAngleToCombinedWind != null) { + finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( + NumberFormat.getFormat("0.0").format(courseDTO.finishLineLengthInMeters), + NumberFormat.getFormat("0.0").format(Math.abs(courseDTO.finishLineAngleToCombinedWind)), + courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), + NumberFormat.getFormat("0.0").format(courseDTO.finishLineAdvantageInMeters))); + } else { + finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } - else { - if (finishLine != null) { - finishLine.setMap(null); - finishLine = null; - } + finishLine = showOrRemoveOrUpdateLine(finishLine, /* showLine */ courseDTO.getFinishMarkPositions() != null && courseDTO.getFinishMarkPositions().size() == 2 && + (oneBasedLegOfLeadingCompetitor > 0 && oneBasedLegOfLeadingCompetitor == numberOfLegs && settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE)) + || settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY), + finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, "#000000"); + // the control point pairs for which we already decided whether or not + // to show a course middle line for; values tell whether to show the line and for which zero-based + // start waypoint index to do so; when for an equal control point pair multiple decisions with different + // outcome are made, a decision to show the line overrules the decision to not show it (OR-semantics) + final Map, Pair> keysAlreadyHandled = new HashMap<>(); + for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint key = getCourseMiddleLinesKey(courseDTO, zeroBasedIndexOfStartWaypoint); + boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() || + settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) + && oneBasedLegOfLeadingCompetitor > 0 + && oneBasedLegOfLeadingCompetitor-1 == zeroBasedIndexOfStartWaypoint); + keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint)); } - // draw the course middle line - if (legOfLeadingCompetitor > 0 && courseDTO.waypointPositions.size() > legOfLeadingCompetitor && - settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE)) { - Position position1DTO = courseDTO.waypointPositions.get(legOfLeadingCompetitor-1); - Position position2DTO = courseDTO.waypointPositions.get(legOfLeadingCompetitor); - LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO); - LatLng courseMiddleLinePoint2 = coordinateSystem.toLatLng(position2DTO); - if (courseMiddleLine == null) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setClickable(true); - options.setGeodesic(true); - options.setStrokeColor("#2268a0"); - options.setStrokeWeight(1); - options.setStrokeOpacity(1.0); - - MVCArray pointsAsArray = MVCArray.newInstance(); - pointsAsArray.insertAt(0, courseMiddleLinePoint1); - pointsAsArray.insertAt(1, courseMiddleLinePoint2); - - courseMiddleLine = Polyline.newInstance(options); - courseMiddleLine.setPath(pointsAsArray); - - courseMiddleLine.addMouseOverHandler(new MouseOverMapHandler() { - @Override - public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.courseMiddleLine()); - } - }); - courseMiddleLine.addMouseOutMoveHandler(new MouseOutMapHandler() { - @Override - public void onEvent(MouseOutMapEvent event) { - map.setTitle(""); - } - }); - courseMiddleLine.setMap(map); - } else { - courseMiddleLine.getPath().removeAt(1); - courseMiddleLine.getPath().removeAt(0); - courseMiddleLine.getPath().insertAt(0, courseMiddleLinePoint1); - courseMiddleLine.getPath().insertAt(1, courseMiddleLinePoint2); - } + Set> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet()); + keysToConsider.addAll(courseMiddleLines.keySet()); + for (final Set key : keysToConsider) { + final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) ? + keysAlreadyHandled.get(key).getB() : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter + final Pair showLineAndZeroBasedIndexOfStartWaypoint = keysAlreadyHandled.get(key); + final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null && showLineAndZeroBasedIndexOfStartWaypoint.getA(); + courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); } - else { - if (courseMiddleLine != null) { - courseMiddleLine.setMap(null); - courseMiddleLine = null; + } + } + + /** + * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that denotes the start + * waypoint of the leg in question, returns a key that can be used for the {@link #courseMiddleLines} map, consisting of a set + * that holds the two {@link ControlPointDTO}s representing the start and finish control point of that leg. + */ + private Set getCourseMiddleLinesKey(final CoursePositionsDTO courseDTO, + final int zeroBasedIndexOfStartWaypoint) { + ControlPointDTO startControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint).controlPoint; + ControlPointDTO endControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint+1).controlPoint; + final Set key = new HashSet<>(); + key.add(startControlPoint); + key.add(endControlPoint); + return key; + } + + /** + * @param showLine + * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a + * line but the line shall not be shown, the line is removed from the map; conversely, if the line is not + * yet shown but shall be, a new line is created, added to the map and returned. If the line is shown and + * shall continue to be shown, the line is returned after updating its vertex coordinates. + * @return null if the line is not shown; the polyline object representing the line being displayed + * otherwise + */ + private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, Polyline lineToShowOrRemoveOrUpdate, + final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { + final Position position1DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint); + final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint+1); + final LineInfoProvider lineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + final StringBuilder sb = new StringBuilder(); + sb.append(stringMessages.courseMiddleLine()); + sb.append('\n'); + sb.append(NumberFormat.getFormat("0").format( + Math.abs(position1DTO.getDistance(position2DTO).getMeters()))+stringMessages.metersUnit()); + if (lastCombinedWindTrackInfoDTO != null) { + final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO.getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); + if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null && !windTrackAtLegMiddle.windFixes.isEmpty()) { + WindDTO windAtLegMiddle = windTrackAtLegMiddle.windFixes.get(0); + final double legBearingDeg = position1DTO.getBearingGreatCircle(position2DTO).getDegrees(); + final String diff = NumberFormat.getFormat("0.0").format( + Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg-legBearingDeg), + Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg-legBearingDeg))); + sb.append(", "); + sb.append(stringMessages.degreesToWind(diff)); + } } + return sb.toString(); + } + }; + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0"); + } + + private interface LineInfoProvider { + String getLineInfo(); + } + + /** + * @param showLine + * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a + * line but the line shall not be shown, the line is removed from the map; conversely, if the line is not + * yet shown but shall be, a new line is created, added to the map and returned. If the line is shown and + * shall continue to be shown, the line is returned after updating its vertex coordinates. + * @return null if the line is not shown; the polyline object representing the line being displayed + * otherwise + */ + private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, + final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB) { + if (showLine) { + LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO); + LatLng courseMiddleLinePoint2 = coordinateSystem.toLatLng(position2DTO); + final MVCArray pointsAsArray; + if (lineToShowOrRemoveOrUpdate == null) { + PolylineOptions options = PolylineOptions.newInstance(); + options.setClickable(true); + options.setGeodesic(true); + options.setStrokeColor(lineColorRGB); + options.setStrokeWeight(1); + options.setStrokeOpacity(1.0); + pointsAsArray = MVCArray.newInstance(); + lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); + lineToShowOrRemoveOrUpdate.setPath(pointsAsArray); + lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() { + @Override + public void onEvent(MouseOverMapEvent event) { + map.setTitle(lineInfoProvider.getLineInfo()); + } + }); + lineToShowOrRemoveOrUpdate.addMouseOutMoveHandler(new MouseOutMapHandler() { + @Override + public void onEvent(MouseOutMapEvent event) { + map.setTitle(""); + } + }); + lineToShowOrRemoveOrUpdate.setMap(map); + + } else { + pointsAsArray = lineToShowOrRemoveOrUpdate.getPath(); + pointsAsArray.removeAt(1); + pointsAsArray.removeAt(0); + } + adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider); + pointsAsArray.insertAt(0, courseMiddleLinePoint1); + pointsAsArray.insertAt(1, courseMiddleLinePoint2); + } else { + if (lineToShowOrRemoveOrUpdate != null) { + lineToShowOrRemoveOrUpdate.setMap(null); + adjustInfoOverlayForRemovedLine(lineToShowOrRemoveOrUpdate); + lineToShowOrRemoveOrUpdate = null; + } + } + return lineToShowOrRemoveOrUpdate; + } + + private void adjustInfoOverlayForRemovedLine(Polyline lineToShowOrRemoveOrUpdate) { + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + if (infoOverlay != null) { + infoOverlay.removeFromMap(); + } + } + + private void adjustInfoOverlayForVisibleLine(Polyline lineToShowOrRemoveOrUpdate, final Position position1DTO, + final Position position2DTO, final LineInfoProvider lineInfoProvider) { + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); + if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { + if (infoOverlay == null) { + infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem); + infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); + infoOverlay.addToMap(); + } else { + infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); + } + infoOverlay.setPosition(position1DTO.translateGreatCircle(position1DTO.getBearingGreatCircle(position2DTO), + position1DTO.getDistance(position2DTO).scale(0.5)), /* transition time */ -1); + infoOverlay.draw(); + } else { + if (infoOverlay != null) { + infoOverlay.removeFromMap(); + infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); } } } @@ -1554,9 +1595,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe * still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the * start line that shows the count down. */ - private void updateCountdownCanvas(List startMarkPositions) { - if (!settings.isShowSelectedCompetitorsInfo() || startMarkPositions == null || startMarkPositions.isEmpty() - || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { + private void updateCountdownCanvas(WaypointDTO startWaypoint) { + if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null || Util.isEmpty(startWaypoint.controlPoint.getMarks()) + || lastRaceTimesInfo == null || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { if (countDownOverlay != null) { countDownOverlay.removeFromMap(); countDownOverlay = null; @@ -1572,7 +1613,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe } else { countDownOverlay.setInfoText(countDownText); } - countDownOverlay.setPosition(startMarkPositions.get(startMarkPositions.size() - 1), -1); + countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, /* transition time */ -1); countDownOverlay.draw(); } } @@ -1610,7 +1651,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) || graticuleAreaRatio(currentMapBounds, newBounds) > 10) { // only change bounds if the new bounds don't fit into the current map zoom - List oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom(); + Iterable oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom(); setAutoZoomInProgress(true); autoZoomLatLngBounds = newBounds; int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); @@ -2358,8 +2399,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe } @Override - public void initializeData(boolean showMapControls) { - loadMapsAPIV3(showMapControls); + public void initializeData(boolean showMapControls, boolean showHeaderPanel) { + loadMapsAPIV3(showMapControls, showHeaderPanel); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapHelpLinesSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapHelpLinesSettings.java index b5c8604b4f1..78e928cc084 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapHelpLinesSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapHelpLinesSettings.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.client.shared.racemap; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -10,7 +11,7 @@ public class RaceMapHelpLinesSettings { * Types of help lines on the map */ public enum HelpLineTypes { - STARTLINE, FINISHLINE, ADVANTAGELINE, COURSEMIDDLELINE, BUOYZONE, BOATTAILS, STARTLINETOFIRSTMARKTRIANGLE + STARTLINE, FINISHLINE, ADVANTAGELINE, COURSEMIDDLELINE, BUOYZONE, BOATTAILS, STARTLINETOFIRSTMARKTRIANGLE, COURSEGEOMETRY } private final Set visibleHelpLines; @@ -35,6 +36,10 @@ public class RaceMapHelpLinesSettings { return visibleHelpLines.contains(helpLineType); } + public Iterable getVisibleHelpLineTypes() { + return Collections.unmodifiableCollection(visibleHelpLines); + } + @Override public int hashCode() { final int prime = 31; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java index 32de30d5e02..1113211fef4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java @@ -98,7 +98,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent(cb, zoomType)); zoomSettingsBoxesPanel.add(cb); @@ -195,6 +195,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent typesToConsiderOnZoom; + private Iterable typesToConsiderOnZoom; private boolean zoomToSelectedCompetitors; /** @@ -37,13 +38,14 @@ public class RaceMapZoomSettings { * The attribute zoomToSelectedCompetitors will be false. */ public RaceMapZoomSettings() { - typesToConsiderOnZoom = new ArrayList(); + final List myTypesToConsiderOnZoom = new ArrayList<>(); + typesToConsiderOnZoom = myTypesToConsiderOnZoom; // Other zoom types such as BOATS, TAILS or WINDSENSORS are not currently used as default zoom types. - typesToConsiderOnZoom.add(ZoomTypes.BUOYS); + myTypesToConsiderOnZoom.add(ZoomTypes.BUOYS); zoomToSelectedCompetitors = false; } - public RaceMapZoomSettings(ArrayList typesToConsiderOnZoom, boolean zoomToSelected) { + public RaceMapZoomSettings(Iterable typesToConsiderOnZoom, boolean zoomToSelected) { this.typesToConsiderOnZoom = typesToConsiderOnZoom; this.zoomToSelectedCompetitors = zoomToSelected; } @@ -67,12 +69,14 @@ public class RaceMapZoomSettings { return newBounds; } - public List getTypesToConsiderOnZoom() { + public Iterable getTypesToConsiderOnZoom() { return typesToConsiderOnZoom; } - public void setTypesToConsiderOnZoom(List typesToConsiderOnZoom) { - this.typesToConsiderOnZoom = new ArrayList(typesToConsiderOnZoom); + public void setTypesToConsiderOnZoom(Iterable typesToConsiderOnZoom) { + List newTypesToConsiderOnZoom = new ArrayList<>(); + Util.addAll(typesToConsiderOnZoom, newTypesToConsiderOnZoom); + this.typesToConsiderOnZoom = newTypesToConsiderOnZoom; } public void setZoomToSelectedCompetitors(boolean zoomToSelectedCompetitors) { @@ -84,7 +88,7 @@ public class RaceMapZoomSettings { } public boolean containsZoomType(ZoomTypes zoomType) { - return typesToConsiderOnZoom.contains(zoomType); + return Util.contains(typesToConsiderOnZoom, zoomType); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java index 70a8344cd51..7846cd7f2b8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java @@ -42,32 +42,45 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { infoBoxWidth = 20; infoBoxHeight = 20; cornerRadius = 4; - if (getCanvas() != null) { getCanvas().setWidth(String.valueOf(canvasWidth)); getCanvas().setHeight(String.valueOf(canvasHeight)); getCanvas().setCoordinateSpaceWidth(canvasWidth); getCanvas().setCoordinateSpaceHeight(canvasHeight); } - -// setCanvasSize(canvasWidth, canvasHeight); } @Override protected void draw() { + final int LINE_SPACING = 3; + final int BOTTOM_MARGIN = 8; + final int LEFT_MARGIN = 8; + final int RIGHT_MARGIN = 9; + final int POLE_LENGTH = 25; if (mapProjection != null && position != null) { LatLng latLngPosition = coordinateSystem.toLatLng(position); Context2d context2d = getCanvas().getContext2d(); CssColor grayTransparentColor = CssColor.make("rgba(255,255,255,0.75)"); - - context2d.setFont("12px bold Verdana sans-serif"); - TextMetrics measureText = context2d.measureText(infoText); - double textWidth = measureText.getWidth(); - - canvasWidth = (int) textWidth + 17; + final int fontSizeInPx = 12+2*Math.max(0, map.getZoom()-15); + final int LINE_HEIGHT = fontSizeInPx+LINE_SPACING; + context2d.setFont(""+fontSizeInPx+"px Roboto, Arial, sans-serif"); +// context2d.setFont(""+fontSizeInPx+"px bold Verdana sans-serif"); + double textWidth = 0; + infoBoxHeight = BOTTOM_MARGIN; + for (final String line : infoText.split("\n")) { + TextMetrics measureText = context2d.measureText(line); + if (measureText.getWidth() > textWidth) { + textWidth = measureText.getWidth(); + } + infoBoxHeight += LINE_HEIGHT; + } + canvasWidth = (int) textWidth + LEFT_MARGIN + RIGHT_MARGIN; infoBoxWidth = canvasWidth; + canvasHeight = infoBoxHeight + POLE_LENGTH; getCanvas().setWidth(String.valueOf(canvasWidth)); getCanvas().setCoordinateSpaceWidth(canvasWidth); + getCanvas().setHeight(String.valueOf(canvasHeight)); + getCanvas().setCoordinateSpaceHeight(canvasHeight); // Change origin and dimensions to match true size (a stroke makes the shape a bit larger) context2d.setFillStyle(grayTransparentColor); @@ -86,7 +99,13 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { context2d.beginPath(); context2d.setFillStyle("black"); - context2d.fillText(infoText, 8, 14); +// context2d.setFont(""+fontSizeInPx+"px bold Verdana sans-serif"); + context2d.setFont(""+fontSizeInPx+"px Roboto, Arial, sans-serif"); + int y = LINE_HEIGHT; + for (final String line : infoText.split("\n")) { + context2d.fillText(line, LEFT_MARGIN, y); + y += LINE_HEIGHT; + } context2d.stroke(); Point objectPositionInPx = mapProjection.fromLatLngToDivPixel(latLngPosition); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/common/client/DateAndTimeFormatterUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/common/client/DateAndTimeFormatterUtil.java index e62fdecddc1..88b4b5207be 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/common/client/DateAndTimeFormatterUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/common/client/DateAndTimeFormatterUtil.java @@ -4,9 +4,12 @@ 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.TimeZone; import com.google.gwt.text.client.DateTimeFormatRenderer; public class DateAndTimeFormatterUtil { + public static TimeZone timeZoneWithoutOffset = TimeZone.createTimeZone(0); + public static DateTimeFormatRenderer defaultDateFormatter = new DateTimeFormatRenderer( DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)); public static DateTimeFormatRenderer defaultTimeFormatter = new DateTimeFormatRenderer( @@ -15,14 +18,14 @@ public class DateAndTimeFormatterUtil { public static DateTimeFormatRenderer longDateFormatter = new DateTimeFormatRenderer( DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_LONG)); public static DateTimeFormatRenderer longTimeFormatter = new DateTimeFormatRenderer( - DateTimeFormat.getFormat("HH:mm:ss zzz")); + DateTimeFormat.getFormat("HH:mm:ss zzz"), timeZoneWithoutOffset); private static DateTimeFormatRenderer secondsTimeFormatter = new DateTimeFormatRenderer( - DateTimeFormat.getFormat("m:ss")); + DateTimeFormat.getFormat("m:ss"), timeZoneWithoutOffset); private static DateTimeFormatRenderer minutesAndSecondsTimeFormatter = new DateTimeFormatRenderer( - DateTimeFormat.getFormat("mm:ss")); + DateTimeFormat.getFormat("mm:ss"), timeZoneWithoutOffset); private static DateTimeFormatRenderer hoursAndMinutesAndSecondsTimeFormatter = new DateTimeFormatRenderer( - DateTimeFormat.getFormat("HH:mm:ss")); + DateTimeFormat.getFormat("HH:mm:ss"), timeZoneWithoutOffset); public static String formatDateRange(Date startDate, Date endDate) { return defaultDateFormatter.render(startDate) + " - " + defaultDateFormatter.render(endDate); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java index 63927ae091d..95a986a0d3f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/DataMiningEntryPoint.java @@ -27,6 +27,7 @@ import com.sap.sse.datamining.shared.impl.UUIDDataMiningSession; import com.sap.sse.gwt.client.EntryPointHelper; import com.sap.sse.gwt.client.shared.components.ComponentResources; import com.sap.sse.gwt.client.shared.components.SettingsDialog; +import com.sap.sse.gwt.resources.Highcharts; import com.sap.sse.gwt.shared.GwtHttpRequestUtils; public class DataMiningEntryPoint extends AbstractSailingEntryPoint { @@ -38,6 +39,7 @@ public class DataMiningEntryPoint extends AbstractSailingEntryPoint { @Override protected void doOnModuleLoad() { + Highcharts.ensureInjectedWithMore(); super.doOnModuleLoad(); session = new UUIDDataMiningSession(UUID.randomUUID()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsEntryPoint.java index 9662192b872..e8828077ec1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsEntryPoint.java @@ -10,6 +10,7 @@ import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; import com.sap.sailing.gwt.ui.client.RegattaRefresher; import com.sap.sailing.gwt.ui.client.RegattasDisplayer; import com.sap.sailing.gwt.ui.shared.RegattaDTO; +import com.sap.sse.gwt.resources.Highcharts; public class PolarSheetsEntryPoint extends AbstractSailingEntryPoint implements RegattaRefresher { @@ -17,6 +18,7 @@ public class PolarSheetsEntryPoint extends AbstractSailingEntryPoint implements @Override protected void doOnModuleLoad() { + Highcharts.ensureInjectedWithMore(); super.doOnModuleLoad(); regattaDisplayers = new HashSet(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java new file mode 100644 index 00000000000..25741adac15 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/EmbeddedMapAndWindChartEntryPoint.java @@ -0,0 +1,216 @@ + +package com.sap.sailing.gwt.ui.raceboard; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.controls.ControlPosition; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.DockLayoutPanel.Direction; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RootLayoutPanel; +import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; +import com.sap.sailing.gwt.ui.client.RaceSelectionModel; +import com.sap.sailing.gwt.ui.client.TimePanel; +import com.sap.sailing.gwt.ui.client.TimePanelSettings; +import com.sap.sailing.gwt.ui.client.shared.charts.WindChart; +import com.sap.sailing.gwt.ui.client.shared.charts.WindChartSettings; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.HelpLineTypes; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapResources; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes; +import com.sap.sailing.gwt.ui.shared.EventDTO; +import com.sap.sse.common.Duration; +import com.sap.sse.common.Util; +import com.sap.sse.common.filter.Filter; +import com.sap.sse.common.filter.FilterSet; +import com.sap.sse.gwt.client.async.AsyncActionsExecutor; +import com.sap.sse.gwt.client.player.TimeRangeWithZoomModel; +import com.sap.sse.gwt.client.player.TimeRangeWithZoomProvider; +import com.sap.sse.gwt.client.player.Timer; +import com.sap.sse.gwt.client.player.Timer.PlayModes; +import com.sap.sse.gwt.shared.GwtHttpRequestUtils; + +public class EmbeddedMapAndWindChartEntryPoint extends AbstractSailingEntryPoint { + private static final String PARAM_REGATTA_LIKE_NAME = "regattaLikeName"; + private static final String PARAM_RACE_COLUMN_NAME = "raceColumnName"; + private static final String PARAM_FLEET_NAME = "fleetName"; + private static final String PARAM_EVENT_ID = "eventId"; + + private String regattaLikeName; + private String raceColumnName; + private String fleetName; + private UUID eventId; + + private static final RaceMapResources raceMapResources = GWT.create(RaceMapResources.class); + private static final int DEFAULT_WIND_CHART_HEIGHT = 200; + + @Override + protected void doOnModuleLoad() { + GWT.debugger(); // TODO remove again + super.doOnModuleLoad(); + // read mandatory parameters + regattaLikeName = Window.Location.getParameter(PARAM_REGATTA_LIKE_NAME); + raceColumnName = Window.Location.getParameter(PARAM_RACE_COLUMN_NAME); + fleetName = Window.Location.getParameter(PARAM_FLEET_NAME); + String eventIdParamValue = Window.Location.getParameter(PARAM_EVENT_ID); + if (eventIdParamValue != null && !eventIdParamValue.isEmpty()) { + eventId = UUID.fromString(eventIdParamValue); + } + if (regattaLikeName == null || regattaLikeName.isEmpty() || raceColumnName == null || raceColumnName.isEmpty() || + fleetName == null || fleetName.isEmpty()) { + createErrorPage("This page requires a valid regatta, race column and fleet name to identify the race to show."); + return; + } + + // read optional parameters + final boolean showWindChart = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_WINDCHART, true /* default*/); + final boolean showViewStreamlets = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_STREAMLETS, false /* default*/); + final boolean showViewSimulation = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_SIMULATION, true /* default*/); + final boolean showMapControls = GwtHttpRequestUtils.getBooleanParameter(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, true /* default*/); + if (eventId != null) { + sailingService.getEventById(eventId, /* withStatisticalData */ false, new AsyncCallback() { + @Override + public void onSuccess(EventDTO eventDTO) { + checkUrlParameters(eventDTO, showWindChart, showMapControls, showViewStreamlets, showViewSimulation); + } + + @Override + public void onFailure(Throwable t) { + reportError("Error trying to create the raceboard: " + t.getMessage()); + } + }); + } else { + checkUrlParameters(/* event */ null, showWindChart, showMapControls, showViewStreamlets, showViewSimulation); + } + } + + private void createErrorPage(String message) { + final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX); + LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService()); + logoAndTitlePanel.addStyleName("LogoAndTitlePanel"); + RootLayoutPanel.get().add(vp); + vp.addNorth(logoAndTitlePanel, 100); + vp.add(new Label(message)); + } + + private void checkUrlParameters(final EventDTO event, final boolean showWindChart, + final boolean showMapControls, final boolean showViewStreamlets, final boolean showViewSimulation) { + if (eventId != null && event == null) { + createErrorPage(getStringMessages().noSuchEvent()); + } + sailingService.getRaceIdentifier(regattaLikeName, raceColumnName, fleetName, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + createErrorPage("Could not obtain a race with name " + raceColumnName + " for fleet "+fleetName+" for a regatta with name " + regattaLikeName+ + ": "+caught.getMessage()); + } + + @Override + public void onSuccess(final RegattaAndRaceIdentifier selectedRaceIdentifier) { + if (selectedRaceIdentifier == null) { + createErrorPage("Could not obtain a race with name " + raceColumnName + " for fleet "+fleetName+" for a regatta with name " + regattaLikeName); + } else { + final StringBuilder title = new StringBuilder(regattaLikeName); + title.append('/'); + title.append(raceColumnName); + if (!fleetName.equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) { + title.append('/'); + title.append(fleetName); + } + Window.setTitle(title.toString()); + final RaceSelectionModel raceSelectionModel = new RaceSelectionModel(); + final List raceList = Collections.singletonList(selectedRaceIdentifier); + raceSelectionModel.setSelection(raceList); + final long refreshInterval = Duration.ONE_SECOND.times(3).asMillis(); + final Timer timer = new Timer(PlayModes.Live); + AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor(); + final TimeRangeWithZoomProvider timeRangeWithZoomProvider = new TimeRangeWithZoomModel(); + // Use a TimePanel to manage wind chart zoom, although the TimePanel itself is not being displayed; + // let the time panel always return to "live" mode if the user wants that + final TimePanel timePanel = new TimePanel( + timer, timeRangeWithZoomProvider, getStringMessages(), /* canReplayWhileLive */ false) { + protected boolean isLiveModeToBeMadePossible() { + return true; + } + }; + final Button backToLivePlayButton = timePanel.getBackToLiveButton(); + final TimePanelSettings timePanelSettings = timePanel.getSettings(); + timePanelSettings.setRefreshInterval(refreshInterval); + timePanel.updateSettings(timePanelSettings); + raceMapResources.combinedWindPanelStyle().ensureInjected(); + final CompetitorSelectionProvider competitorSelection = createEmptyFilterCompetitorModel(); // show no competitors + final RaceMap raceMap = new RaceMap(sailingService, asyncActionsExecutor, /* errorReporter */ EmbeddedMapAndWindChartEntryPoint.this, timer, + competitorSelection, getStringMessages(), showMapControls, showViewStreamlets, + showViewSimulation, selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle(), /* showHeaderPanel */ false) { + @Override + protected void showAdditionalControls(MapWidget map) { + backToLivePlayButton.removeFromParent(); + map.setControls(ControlPosition.RIGHT_BOTTOM, backToLivePlayButton); + } + }; + final RaceMapSettings mapSettings = raceMap.getSettings(); + mapSettings.setZoomSettings(new RaceMapZoomSettings(Arrays.asList(ZoomTypes.BUOYS), /* zoom to selection */ false)); + Set helpLineTypes = new HashSet<>(); + Util.addAll(mapSettings.getHelpLinesSettings().getVisibleHelpLineTypes(), helpLineTypes); + helpLineTypes.add(HelpLineTypes.COURSEGEOMETRY); + mapSettings.setHelpLinesSettings(new RaceMapHelpLinesSettings(helpLineTypes)); + raceMap.updateSettings(mapSettings); + raceMap.onRaceSelectionChange(raceList); + final WindChart windChart; + if (showWindChart) { + windChart = new WindChart(sailingService, raceSelectionModel, timer, + timeRangeWithZoomProvider, new WindChartSettings(), getStringMessages(), + asyncActionsExecutor, /* errorReporter */ + EmbeddedMapAndWindChartEntryPoint.this, /* compactChart */ true); + windChart.onRaceSelectionChange(raceList); + } else { + windChart = null; + } + createRaceBoardInOneScreenMode(raceMap, windChart); + } + } + }); + } + + private CompetitorSelectionProvider createEmptyFilterCompetitorModel() { + final CompetitorSelectionModel result = new CompetitorSelectionModel(/* hasMultiSelection */ true); + final FilterSet> filterSet = result.getOrCreateCompetitorsFilterSet("Empty"); + filterSet.addFilter(new Filter() { + @Override public boolean matches(CompetitorDTO object) { return false; } + @Override public String getName() { return "Never matching filter"; } + }); + return result; + } + + private void createRaceBoardInOneScreenMode(final RaceMap raceMap, final WindChart windChart) { + final TouchSplitLayoutPanel p = new TouchSplitLayoutPanel(/* horizontal splitter width */ 3, /* vertical splitter height */ 25); + RootLayoutPanel.get().add(p); + if (windChart != null) { + windChart.setVisible(true); + p.insert(windChart.getEntryWidget(), windChart, Direction.SOUTH, DEFAULT_WIND_CHART_HEIGHT); + p.setWidgetVisibility(windChart.getEntryWidget(), windChart, /* hidden */ false, DEFAULT_WIND_CHART_HEIGHT); + } + p.insert(raceMap.getEntryWidget(), raceMap, Direction.CENTER, 400); + p.addStyleName("dockLayoutPanel"); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java index 13512300c2d..7d9e1c75e65 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardEntryPoint.java @@ -17,7 +17,6 @@ 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; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.dto.RaceDTO; import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; @@ -138,10 +137,12 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { } private void createErrorPage(String message) { + final DockLayoutPanel vp = new DockLayoutPanel(Unit.PX); LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages(), this, getUserService()); logoAndTitlePanel.addStyleName("LogoAndTitlePanel"); - RootPanel.get().add(logoAndTitlePanel); - RootPanel.get().add(new Label(message)); + RootLayoutPanel.get().add(vp); + vp.addNorth(logoAndTitlePanel, 100); + vp.add(new Label(message)); } private void checkUrlParameters(List leaderboardNames, LeaderboardGroupDTO leaderboardGroup, @@ -194,7 +195,6 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint { RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, singletonList, 5000l /* requestInterval*/); RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, getUserService(), asyncActionsExecutor, timer, raceSelectionModel, leaderboardName, leaderboardGroupName, event, raceboardViewConfig, RaceBoardEntryPoint.this, getStringMessages(), userAgent, raceTimesInfoProvider, showMapControls); - raceBoardPanel.fillRegattas(regattas); createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java index 9af624c6d49..6d89dae29d3 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java @@ -35,7 +35,6 @@ import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.RaceSelectionProvider; import com.sap.sailing.gwt.ui.client.RaceTimePanel; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; -import com.sap.sailing.gwt.ui.client.RegattasDisplayer; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent; @@ -54,7 +53,6 @@ import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory; import com.sap.sailing.gwt.ui.shared.EventDTO; -import com.sap.sailing.gwt.ui.shared.RegattaDTO; import com.sap.sse.common.filter.FilterSet; import com.sap.sse.common.settings.AbstractSettings; import com.sap.sse.gwt.client.ErrorReporter; @@ -77,7 +75,7 @@ import com.sap.sse.security.ui.client.UserService; * @author Frank Mittag, Axel Uhl (d043530) * */ -public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, RaceSelectionChangeListener, LeaderboardUpdateListener, PopupPositionProvider { +public class RaceBoardPanel extends SimplePanel implements RaceSelectionChangeListener, LeaderboardUpdateListener, PopupPositionProvider { private final SailingServiceAsync sailingService; private final MediaServiceAsync mediaService; private final EventDTO event; @@ -86,11 +84,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra private final RaceBoardViewConfiguration raceboardViewConfiguration; private String raceBoardName; - /** - * Updated upon each {@link #fillRegattas(List)} - */ - private final Map racesByIdentifier; - private final List componentViewers; private RaceTimePanel timePanel; private final Timer timer; @@ -146,7 +139,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra this.currentRaceHasBeenSelectedOnce = false; this.leaderboardName = leaderboardName; raceSelectionProvider.addRaceSelectionChangeListener(this); - racesByIdentifier = new HashMap(); selectedRaceIdentifier = raceSelectionProvider.getSelectedRaces().iterator().next(); this.setRaceBoardName(selectedRaceIdentifier.getRaceName()); this.asyncActionsExecutor = asyncActionsExecutor; @@ -163,7 +155,8 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra raceMapResources.combinedWindPanelStyle().ensureInjected(); raceMap = new RaceMap(sailingService, asyncActionsExecutor, errorReporter, timer, - competitorSelectionModel, stringMessages, showMapControls, getConfiguration().isShowViewStreamlets(), getConfiguration().isShowViewSimulation(), selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle()); + competitorSelectionModel, stringMessages, showMapControls, getConfiguration().isShowViewStreamlets(), getConfiguration().isShowViewSimulation(), + selectedRaceIdentifier, raceMapResources.combinedWindPanelStyle(), /* showHeaderPanel */ true); CompetitorFilterPanel competitorSearchTextBox = new CompetitorFilterPanel(competitorSelectionModel, stringMessages, raceMap, new LeaderboardFetcher() { @Override @@ -336,18 +329,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra return errorReporter; } - @Override - public void fillRegattas(Iterable regattas) { - racesByIdentifier.clear(); - for (RegattaDTO regatta : regattas) { - for (RaceDTO race : regatta.races) { - if (race != null && race.getRaceIdentifier() != null) { - racesByIdentifier.put(race.getRaceIdentifier(), race); - } - } - } - } - @Override public void onRaceSelectionChange(List selectedRaces) { } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 2d64fd772c7..98539073afe 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -772,7 +772,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private MarkDTO convertToMarkDTO(Mark mark, Position position) { MarkDTO markDTO; - if(position != null) { + if (position != null) { markDTO = new MarkDTO(mark.getId().toString(), mark.getName(), position.getLatDeg(), position.getLngDeg()); } else { markDTO = new MarkDTO(mark.getId().toString(), mark.getName()); @@ -902,7 +902,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S CourseBase lastCourse = state.getCourseDesign(); if (lastCourse != null) { - raceInfoDTO.lastCourseDesign = convertCourseDesignToRaceCourseDTO(lastCourse); + raceInfoDTO.lastCourseDesign = convertToRaceCourseDTO(lastCourse, /* trackedRace */ null, /* timePoint */ null); raceInfoDTO.lastCourseName = lastCourse.getName(); } @@ -952,20 +952,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceInfoDTO.startProcedureDTO = info; } - private RaceCourseDTO convertCourseDesignToRaceCourseDTO(CourseBase lastCourseDesign) { - RaceCourseDTO result = new RaceCourseDTO(Collections. emptyList()); - if (lastCourseDesign != null) { - List waypointDTOs = new ArrayList(); - for (Waypoint waypoint : lastCourseDesign.getWaypoints()) { - ControlPointDTO controlPointDTO = convertToControlPointDTO(waypoint.getControlPoint()); - WaypointDTO waypointDTO = new WaypointDTO(waypoint.getName(), controlPointDTO, waypoint.getPassingInstructions()); - waypointDTOs.add(waypointDTO); - } - result = new RaceCourseDTO(waypointDTOs); - } - return result; - } - private List convertToRaceDTOs(Regatta regatta) { List result = new ArrayList(); for (RaceDefinition r : regatta.getAllRaces()) { @@ -1864,6 +1850,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S result.totalLegsCount = trackedRace.getRace().getCourse().getLegs().size(); result.currentLegNumber = trackedRace.getLastLegStarted(dateAsTimePoint); result.marks = new HashSet(); + result.course = convertToRaceCourseDTO(trackedRace.getRace().getCourse(), trackedRace, dateAsTimePoint); + // now make sure we don't duplicate the MarkDTO objects but instead use the ones from the RaceCourseDTO + // object and amend them with the Position result.waypointPositions = new ArrayList<>(); Set marks = new HashSet(); Course course = trackedRace.getRace().getCourse(); @@ -1876,11 +1865,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S marks.add(b); } } - for (Mark mark : marks) { - GPSFixTrack track = trackedRace.getOrCreateTrack(mark); - Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false); - if (positionAtDate != null) { - result.marks.add(convertToMarkDTO(mark, positionAtDate)); + for (final WaypointDTO waypointDTO : result.course.waypoints) { + for (final MarkDTO markDTO : waypointDTO.controlPoint.getMarks()) { + if (markDTO.position != null) { + result.marks.add(markDTO); + } } } @@ -1889,8 +1878,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (firstWaypoint != null && Util.size(firstWaypoint.getMarks()) == 2) { final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getStartLine(dateAsTimePoint); if (markPositionDTOsAndLineAdvantage != null) { - final List startMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace, firstWaypoint); - result.startMarkPositions = startMarkPositionDTOs; result.startLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters(); Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage .getAbsoluteAngleDifferenceToTrueWind(); @@ -1906,8 +1893,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (lastWaypoint != null && Util.size(lastWaypoint.getMarks()) == 2) { final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getFinishLine(dateAsTimePoint); if (markPositionDTOsAndLineAdvantage != null) { - final List finishMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace, lastWaypoint); - result.finishMarkPositions = finishMarkPositionDTOs; result.finishLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters(); Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage .getAbsoluteAngleDifferenceToTrueWind(); @@ -1952,32 +1937,22 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private ControlPointDTO convertToControlPointDTO(ControlPoint controlPoint, TrackedRace trackedRace, TimePoint timePoint) { ControlPointDTO result; + final TimePoint timePointToUse = trackedRace == null ? null : + timePoint == null ? MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis()) : timePoint; if (controlPoint instanceof ControlPointWithTwoMarks) { final Mark left = ((ControlPointWithTwoMarks) controlPoint).getLeft(); - final Position leftPos = trackedRace.getOrCreateTrack(left).getEstimatedPosition(timePoint, /* extrapolate */ false); + final Position leftPos = timePointToUse == null ? null : trackedRace.getOrCreateTrack(left).getEstimatedPosition(timePointToUse, /* extrapolate */ false); final Mark right = ((ControlPointWithTwoMarks) controlPoint).getRight(); - final Position rightPos = trackedRace.getOrCreateTrack(right).getEstimatedPosition(timePoint, /* extrapolate */ false); + final Position rightPos = timePointToUse == null ? null : trackedRace.getOrCreateTrack(right).getEstimatedPosition(timePointToUse, /* extrapolate */ false); result = new GateDTO(controlPoint.getId().toString(), controlPoint.getName(), convertToMarkDTO(left, leftPos), convertToMarkDTO(right, rightPos)); } else { - final Position posOfFirst = trackedRace.getOrCreateTrack(controlPoint.getMarks().iterator().next()). - getEstimatedPosition(timePoint, /* extrapolate */ false); + final Position posOfFirst = timePointToUse == null ? null : trackedRace.getOrCreateTrack(controlPoint.getMarks().iterator().next()). + getEstimatedPosition(timePointToUse, /* extrapolate */ false); result = new MarkDTO(controlPoint.getId().toString(), controlPoint.getName(), posOfFirst == null ? -1 : posOfFirst.getLatDeg(), posOfFirst == null ? -1 : posOfFirst.getLngDeg()); } return result; } - private ControlPointDTO convertToControlPointDTO(ControlPoint controlPoint) { - ControlPointDTO result; - if (controlPoint instanceof ControlPointWithTwoMarks) { - final Mark left = ((ControlPointWithTwoMarks) controlPoint).getLeft(); - final Mark right = ((ControlPointWithTwoMarks) controlPoint).getRight(); - result = new GateDTO(controlPoint.getId().toString(), controlPoint.getName(), convertToMarkDTO(left, null), convertToMarkDTO(right, null)); - } else { - result = new MarkDTO(controlPoint.getId().toString(), controlPoint.getName()); - } - return result; - } - private ControlPoint getOrCreateControlPoint(ControlPointDTO dto) { String idAsString = dto.getIdAsString(); if (idAsString == null) { @@ -2017,19 +1992,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } } - private List getMarkPositionDTOs( - TimePoint timePoint, TrackedRace trackedRace, Waypoint waypoint) { - List markPositionDTOs = new ArrayList<>(); - for (Mark startMark : waypoint.getMarks()) { - final Position estimatedMarkPosition = trackedRace.getOrCreateTrack(startMark) - .getEstimatedPosition(timePoint, /* extrapolate */false); - if (estimatedMarkPosition != null) { - markPositionDTOs.add(estimatedMarkPosition); - } - } - return markPositionDTOs; - } - /** * @param timePoint * null means "live" and is then replaced by "now" minus the tracked race's @@ -5090,36 +5052,56 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceLog.add(event); } - private WaypointDTO convertToWaypointDTO(Waypoint waypoint, Map controlPointCache) { + /** + * @param trackedRace + * if null, no position data will be attached to the {@link MarkDTO}s + * @param timePoint + * if trackedRace is not null, specifies the time point for which to determine + * the mark positions and attach to the {@link MarkDTO}s. If null, the current time point + * will be used as default. + */ + private WaypointDTO convertToWaypointDTO(Waypoint waypoint, Map controlPointCache, TrackedRace trackedRace, TimePoint timePoint) { ControlPointDTO cp = controlPointCache.get(waypoint.getControlPoint().getId()); if (cp == null) { - cp = convertToControlPointDTO(waypoint.getControlPoint()); + cp = convertToControlPointDTO(waypoint.getControlPoint(), trackedRace, timePoint); controlPointCache.put(waypoint.getControlPoint().getId(), cp); } return new WaypointDTO(waypoint.getName(), cp, waypoint.getPassingInstructions()); } - private RaceCourseDTO convertToRaceCourseDTO(CourseBase course) { - List waypointDTOs = new ArrayList(); - Map controlPointCache = new HashMap<>(); - RaceCourseDTO result = new RaceCourseDTO(waypointDTOs); - for (Waypoint waypoint : course.getWaypoints()) { - waypointDTOs.add(convertToWaypointDTO(waypoint, controlPointCache)); + /** + * @param course + * the course to convert + * @param trackedRace + * if null, no position data will be attached to the {@link MarkDTO}s + * @param timePoint + * if trackedRace is not null, specifies the time point for which to determine + * the mark positions and attach to the {@link MarkDTO}s. If null, the current time point + * will be used as default. + */ + private RaceCourseDTO convertToRaceCourseDTO(CourseBase course, TrackedRace trackedRace, TimePoint timePoint) { + final RaceCourseDTO result; + if (course != null) { + List waypointDTOs = new ArrayList(); + Map controlPointCache = new HashMap<>(); + for (Waypoint waypoint : course.getWaypoints()) { + waypointDTOs.add(convertToWaypointDTO(waypoint, controlPointCache, trackedRace, timePoint)); + } + result = new RaceCourseDTO(waypointDTOs); + } else { + result = new RaceCourseDTO(Collections. emptyList()); } return result; } @Override - public RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, - String fleetName) { + public RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName) { RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName); - CourseBase lastPublishedCourse = new LastPublishedCourseDesignFinder(raceLog).analyze(); if (lastPublishedCourse == null) { lastPublishedCourse = new CourseDataImpl(""); } - - return convertToRaceCourseDTO(lastPublishedCourse); + return convertToRaceCourseDTO(lastPublishedCourse, /* trackedRace */ null, /* timePoint */ null); } @Override @@ -5765,4 +5747,23 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return true; } } + + @Override + public RegattaAndRaceIdentifier getRaceIdentifier(String regattaLikeName, String raceColumnName, String fleetName) { + RegattaAndRaceIdentifier result = null; + final Leaderboard leaderboard = getService().getLeaderboardByName(regattaLikeName); + if (leaderboard != null) { + final RaceColumn raceColumn = leaderboard.getRaceColumnByName(raceColumnName); + if (raceColumn != null) { + final Fleet fleet = raceColumn.getFleetByName(fleetName); + if (fleet != null) { + final TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); + if (trackedRace != null) { + result = trackedRace.getRaceIdentifier(); + } + } + } + } + return result; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java index a5d53429fba..f5014136ab8 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CoursePositionsDTO.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.shared; +import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -8,9 +9,8 @@ import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.Position; public class CoursePositionsDTO implements IsSerializable { + public RaceCourseDTO course; public List waypointPositions; - public List startMarkPositions; - public List finishMarkPositions; public Set marks; public int totalLegsCount; @@ -35,4 +35,24 @@ public class CoursePositionsDTO implements IsSerializable { public NauticalSide finishLineAdvantageousSide; public Double finishLineAdvantageInMeters; public Double finishLineLengthInMeters; + + public List getStartMarkPositions() { + final List result = new ArrayList<>(); + if (course != null && course.waypoints != null && !course.waypoints.isEmpty()) { + for (final MarkDTO mark : course.waypoints.get(0).controlPoint.getMarks()) { + result.add(mark.position); + } + } + return result; + } + + public List getFinishMarkPositions() { + final List result = new ArrayList<>(); + if (course != null && course.waypoints != null && !course.waypoints.isEmpty()) { + for (final MarkDTO mark : course.waypoints.get(course.waypoints.size()-1).controlPoint.getMarks()) { + result.add(mark.position); + } + } + return result; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GateDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GateDTO.java index 5c2930674f7..79f777fe846 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GateDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/GateDTO.java @@ -27,4 +27,45 @@ public class GateDTO extends ControlPointDTO { public Iterable getMarks() { return Arrays.asList(left, right); } + + @Override + public int hashCode() { + final int prime = 31; + int result = prime * getIdAsString().hashCode() * getName().hashCode(); + result = prime * result + ((left == null) ? 0 : left.hashCode()); + result = prime * result + ((right == null) ? 0 : right.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (getClass() != obj.getClass()) + return false; + GateDTO other = (GateDTO) obj; + if (getIdAsString() == null) { + if (other.getIdAsString() != null) + return false; + } else if (!getIdAsString().equals(other.getIdAsString())) + return false; + if (getName() == null) { + if (other.getName() != null) + return false; + } else if (!getName().equals(other.getName())) + return false; + if (left == null) { + if (other.left != null) + return false; + } else if (!left.equals(other.left)) + return false; + if (right == null) { + if (other.right != null) + return false; + } else if (!right.equals(other.right)) + return false; + return true; + } + + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java index 9ddb68d97b0..2215b45c264 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceCourseDTO.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.shared; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -21,7 +22,21 @@ public class RaceCourseDTO implements IsSerializable { } public RaceCourseDTO(List waypoints) { - this(waypoints, null); + this(waypoints, getMarksFromWaypoints(waypoints)); + } + + private static List getMarksFromWaypoints(List waypoints) { + final LinkedHashSet marks = new LinkedHashSet<>(); + for (final WaypointDTO waypoint : waypoints) { + for (final MarkDTO m : waypoint.controlPoint.getMarks()) { + marks.add(m); + } + } + final List result = new ArrayList<>(); + for (final MarkDTO m : marks) { + result.add(m); + } + return result; } public RaceCourseDTO(List waypoints, List allMarks) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/CollectionResult.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/CollectionResult.java new file mode 100644 index 00000000000..fa132a5a098 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/CollectionResult.java @@ -0,0 +1,7 @@ +package com.sap.sailing.gwt.ui.shared.dispatch; + +import java.util.Collection; + +public interface CollectionResult extends DTO, Result { + Collection getValues(); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/ListResult.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/ListResult.java index 467f6d2235e..6df09bc8878 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/ListResult.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/ListResult.java @@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.shared.dispatch; import java.util.ArrayList; import java.util.List; -public class ListResult implements DTO, Result { +public class ListResult implements CollectionResult { private ArrayList values = new ArrayList<>(); @SuppressWarnings("unused") diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/SortedSetResult.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/SortedSetResult.java new file mode 100644 index 00000000000..7fb41164a53 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/SortedSetResult.java @@ -0,0 +1,30 @@ +package com.sap.sailing.gwt.ui.shared.dispatch; + +import java.util.Collection; +import java.util.Set; +import java.util.TreeSet; + +public class SortedSetResult implements CollectionResult { + private TreeSet values = new TreeSet<>(); + + public SortedSetResult() { + } + + public SortedSetResult(Collection values) { + this.values.addAll(values); + } + + public void addValue(T value) { + if (value != null) { + this.values.add(value); + } + } + + public Set getValues() { + return values; + } + + public boolean isEmpty() { + return values.isEmpty(); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventActionUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventActionUtil.java index 8876b6a10e3..73f6143294d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventActionUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventActionUtil.java @@ -12,6 +12,7 @@ import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; import com.sap.sailing.gwt.ui.shared.dispatch.DispatchException; import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; import com.sap.sailing.gwt.ui.shared.general.EventState; +import com.sap.sailing.server.RacingEventService; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -35,27 +36,42 @@ public final class EventActionUtil { } public static LeaderboardContext getOverallLeaderboardContext(DispatchContext context, UUID eventId) { - Event event = context.getRacingEventService().getEvent(eventId); + RacingEventService service = context.getRacingEventService(); + Event event = service.getEvent(eventId); if(!HomeServiceUtil.isFakeSeries(event)) { throw new DispatchException("The given event is not a series event."); } LeaderboardGroup leaderboardGroup = Util.get(event.getLeaderboardGroups(), 0); Leaderboard overallLeaderboard = leaderboardGroup.getOverallLeaderboard(); - return new LeaderboardContext(event, leaderboardGroup, overallLeaderboard); + return new LeaderboardContext(service, event, leaderboardGroup, overallLeaderboard); } public static LeaderboardContext getLeaderboardContext(DispatchContext context, UUID eventId, String leaderboardId) { - Event event = context.getRacingEventService().getEvent(eventId); + RacingEventService service = context.getRacingEventService(); + Event event = service.getEvent(eventId); for(LeaderboardGroup leaderboardGroup : event.getLeaderboardGroups()) { for(Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { if(leaderboard.getName().equals(leaderboardId)) { - return new LeaderboardContext(event, leaderboardGroup, leaderboard); + return new LeaderboardContext(service, event, leaderboardGroup, leaderboard); } } } throw new DispatchException("The leaderboard is not part of the given event."); } + public static long getEventStateDependentTTL(DispatchContext context, UUID eventId, long liveTTL) { + Event event = context.getRacingEventService().getEvent(eventId); + return getEventStateDependentTTL(event, liveTTL); + } + + private static long getEventStateDependentTTL(Event event, long liveTTL) { + EventState eventState = HomeServiceUtil.calculateEventState(event); + if(eventState == EventState.RUNNING) { + return liveTTL; + } + return calculateTtlForNonLiveEvent(event, eventState); + } + public static ResultWithTTL withLiveRaceOrDefaultSchedule(DispatchContext context, UUID eventId, CalculationWithEvent callback) { Event event = context.getRacingEventService().getEvent(eventId); EventState eventState = HomeServiceUtil.calculateEventState(event); @@ -87,10 +103,11 @@ public final class EventActionUtil { } public static void forLeaderboardsOfEvent(DispatchContext context, UUID eventId, LeaderboardCallback callback) { - Event event = context.getRacingEventService().getEvent(eventId); + RacingEventService service = context.getRacingEventService(); + Event event = service.getEvent(eventId); for (LeaderboardGroup leaderboardGroup : event.getLeaderboardGroups()) { for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) { - callback.doForLeaderboard(new LeaderboardContext(event, leaderboardGroup, leaderboard)); + callback.doForLeaderboard(new LeaderboardContext(service, event, leaderboardGroup, leaderboard)); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventViewAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventViewAction.java index 935e4c3f7c1..dc1af421909 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventViewAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventViewAction.java @@ -64,7 +64,7 @@ public class GetEventViewAction implements Action { if(isFakeSeries && !HomeServiceUtil.isPartOfEvent(event, leaderboard)) { continue; } - RegattaMetadataDTO regattaDTO = HomeServiceUtil.toRegattaMetadataDTO(leaderboardGroup, leaderboard); + RegattaMetadataDTO regattaDTO = HomeServiceUtil.toRegattaMetadataDTO(event, leaderboardGroup, leaderboard); dto.getRegattas().add(regattaDTO); } catch (Exception e) { logger.log(Level.SEVERE, "Catched exception while reading data for leaderboard " + leaderboard.getName(), e); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetFinishedRacesAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetFinishedRacesAction.java new file mode 100644 index 00000000000..a3374c35709 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetFinishedRacesAction.java @@ -0,0 +1,34 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import java.util.UUID; + +import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.ui.shared.dispatch.Action; +import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; +import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; + +public class GetFinishedRacesAction implements Action>> { + + private UUID eventId; + private String regattaId; + + @SuppressWarnings("unused") + private GetFinishedRacesAction() { + } + + public GetFinishedRacesAction(UUID eventId, String regattaId) { + this.eventId = eventId; + this.regattaId = regattaId; + } + + @Override + @GwtIncompatible + public ResultWithTTL> execute(DispatchContext context) { + RaceListDataCalculator raceListDataCalculator = new RaceListDataCalculator(); + EventActionUtil.forRacesOfRegatta(context, eventId, regattaId, raceListDataCalculator); + + return new ResultWithTTL<>(EventActionUtil.getEventStateDependentTTL(context, eventId, 5 * 60 * 1000), + new SortedSetResult<>(raceListDataCalculator.getResult())); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForEventAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForEventAction.java index d5b9ecc2e03..c198ec41807 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForEventAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForEventAction.java @@ -7,9 +7,10 @@ import com.sap.sailing.domain.base.Event; import com.sap.sailing.gwt.ui.shared.dispatch.Action; import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.CalculationWithEvent; -public class GetLiveRacesForEventAction implements Action> { +public class GetLiveRacesForEventAction implements Action>> { private UUID eventId; @SuppressWarnings("unused") @@ -22,16 +23,14 @@ public class GetLiveRacesForEventAction implements Action execute(final DispatchContext context) { - ResultWithTTL result = EventActionUtil.withLiveRaceOrDefaultSchedule(context, eventId, new CalculationWithEvent() { + public ResultWithTTL> execute(final DispatchContext context) { + return EventActionUtil.withLiveRaceOrDefaultSchedule(context, eventId, new CalculationWithEvent>() { @Override - public ResultWithTTL calculateWithEvent(Event event) { + public ResultWithTTL> calculateWithEvent(Event event) { LiveRaceCalculator liveRaceCalculator = new LiveRaceCalculator(); EventActionUtil.forRacesOfEvent(context, eventId, liveRaceCalculator); - ResultWithTTL result = liveRaceCalculator.getResult(); - return result; + return liveRaceCalculator.getResult(); } }); - return result; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForRegattaAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForRegattaAction.java index e84e049e145..5f86bad356c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForRegattaAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetLiveRacesForRegattaAction.java @@ -7,9 +7,10 @@ import com.sap.sailing.domain.base.Event; import com.sap.sailing.gwt.ui.shared.dispatch.Action; import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.CalculationWithEvent; -public class GetLiveRacesForRegattaAction implements Action> { +public class GetLiveRacesForRegattaAction implements Action>> { private UUID eventId; private String regattaName; @@ -24,10 +25,10 @@ public class GetLiveRacesForRegattaAction implements Action execute(final DispatchContext context) { - return EventActionUtil.withLiveRaceOrDefaultSchedule(context, eventId, new CalculationWithEvent() { + public ResultWithTTL> execute(final DispatchContext context) { + return EventActionUtil.withLiveRaceOrDefaultSchedule(context, eventId, new CalculationWithEvent>() { @Override - public ResultWithTTL calculateWithEvent(Event event) { + public ResultWithTTL> calculateWithEvent(Event event) { LiveRaceCalculator liveRaceCalculator = new LiveRaceCalculator(); EventActionUtil.forRacesOfRegatta(context, eventId, regattaName, liveRaceCalculator); return liveRaceCalculator.getResult(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRaceListViewAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRaceListViewAction.java deleted file mode 100644 index b6f4536a513..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRaceListViewAction.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.dispatch.event; - -import java.util.UUID; - -import com.google.gwt.core.shared.GwtIncompatible; -import com.sap.sailing.gwt.ui.shared.dispatch.Action; -import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; -import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; - -public class GetRaceListViewAction implements Action> { - @SuppressWarnings("unused") - private GetRaceListViewAction() { - } - - public GetRaceListViewAction(UUID eventId) { - } - - @Override - @GwtIncompatible - public ResultWithTTL execute(DispatchContext context) { - final RaceListViewDTO result = new RaceListViewDTO(); - -// RacesActionUtil.forRacesOfEvent(context, eventId, new RaceCallback() { -// @Override -// public void doForRace(RaceContext rc) { -// // TODO better condition after Frank implemented race state stuff -// if(!rc.isRaceDefinitionAvailable() || !rc.isRaceLogAvailable() || !rc.isLive() || !rc.isStartTimeAvailable()) { -// return; -// } -// -// result.addRace(rc.getLiveRaceDTO()); -// } -// }); - return new ResultWithTTL<>(5000, result); - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattaListViewAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattaListViewAction.java new file mode 100644 index 00000000000..6c0781712dc --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattaListViewAction.java @@ -0,0 +1,38 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; + +import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.ui.shared.dispatch.Action; +import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; +import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; +import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.LeaderboardCallback; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; + +public class GetRegattaListViewAction implements Action>> { + private UUID eventId; + + @SuppressWarnings("unused") + private GetRegattaListViewAction() { + } + + public GetRegattaListViewAction(UUID eventId) { + this.eventId = eventId; + } + + @Override + @GwtIncompatible + public ResultWithTTL> execute(final DispatchContext context) { + final List result = new LinkedList<>(); + EventActionUtil.forLeaderboardsOfEvent(context, eventId, new LeaderboardCallback() { + @Override + public void doForLeaderboard(LeaderboardContext leaderboardContext) { + result.add(leaderboardContext.getRegattaWithProgress(context)); + } + }); + return new ResultWithTTL<>(EventActionUtil.getEventStateDependentTTL(context, eventId, 3 * 60 * 1000), new SortedSetResult<>(result)); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattaWithProgressAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattaWithProgressAction.java new file mode 100644 index 00000000000..25b39c4a25b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattaWithProgressAction.java @@ -0,0 +1,31 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import java.util.UUID; + +import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.ui.shared.dispatch.Action; +import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; +import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; + +public class GetRegattaWithProgressAction implements Action> { + + private UUID eventId; + private String regattaId; + + @SuppressWarnings("unused") + private GetRegattaWithProgressAction() { + } + + public GetRegattaWithProgressAction(UUID eventId, String regattaId) { + this.eventId = eventId; + this.regattaId = regattaId; + } + + @Override + @GwtIncompatible + public ResultWithTTL execute(DispatchContext context) { + return new ResultWithTTL<>(EventActionUtil.getEventStateDependentTTL(context, eventId, 5 * 60 * 1000), + EventActionUtil.getLeaderboardContext(context, eventId, regattaId).getRegattaWithProgress(context)); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattasAndLiveRacesForEventAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattasAndLiveRacesForEventAction.java index d0b284d107c..55f7c25a193 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattasAndLiveRacesForEventAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetRegattasAndLiveRacesForEventAction.java @@ -69,7 +69,7 @@ public class GetRegattasAndLiveRacesForEventAction implements Action result = new HashMap<>(); for (LeaderboardGroup lg : event.getLeaderboardGroups()) { for (Leaderboard lb : lg.getLeaderboards()) { - result.put(lb.getName(), HomeServiceUtil.toRegattaMetadataDTO(lg, lb)); + result.put(lb.getName(), HomeServiceUtil.toRegattaMetadataDTO(event, lg, lb)); } } return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LeaderboardContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LeaderboardContext.java index 6af21dc5324..b50a9943712 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LeaderboardContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LeaderboardContext.java @@ -18,17 +18,19 @@ import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; import com.sap.sailing.gwt.ui.shared.dispatch.DispatchException; import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.RaceCallback; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaWithProgressDTO; import com.sap.sailing.server.RacingEventService; public class LeaderboardContext { private static final Logger logger = Logger.getLogger(LeaderboardContext.class.getName()); private final Event event; - @SuppressWarnings("unused") private final LeaderboardGroup leaderboardGroup; private final Leaderboard leaderboard; + private final RacingEventService service; - public LeaderboardContext(Event event, LeaderboardGroup leaderboardGroup, Leaderboard leaderboard) { + public LeaderboardContext(RacingEventService service, Event event, LeaderboardGroup leaderboardGroup, Leaderboard leaderboard) { + this.service = service; this.event = event; this.leaderboardGroup = leaderboardGroup; this.leaderboard = leaderboard; @@ -37,11 +39,19 @@ public class LeaderboardContext { public void forRaces(DispatchContext context, RaceCallback callback) { for(RaceColumn raceColumn : leaderboard.getRaceColumns()) { for(Fleet fleet : raceColumn.getFleets()) { - callback.doForRace(new RaceContext(event, leaderboard, raceColumn, fleet, context.getRacingEventService())); + callback.doForRace(new RaceContext(service, event, leaderboard, raceColumn, fleet, context.getRacingEventService())); } } } + public RegattaWithProgressDTO getRegattaWithProgress(DispatchContext context) { + RegattaProgressCalculator regattaProgressCalculator = new RegattaProgressCalculator(); + forRaces(context, regattaProgressCalculator); + RegattaWithProgressDTO regattaDTO = new RegattaWithProgressDTO(regattaProgressCalculator.getResult()); + HomeServiceUtil.fillRegattaFields(event, leaderboardGroup, leaderboard, regattaDTO); + return regattaDTO; + } + public ResultWithTTL calculateMiniLeaderboard(RacingEventService service, int limit) { GetMiniLeaderboardDTO result = new GetMiniLeaderboardDTO(); if (leaderboard == null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceCalculator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceCalculator.java index 46636654ee4..3fe48bdb6cf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceCalculator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceCalculator.java @@ -2,22 +2,25 @@ package com.sap.sailing.gwt.ui.shared.dispatch.event; import com.google.gwt.core.shared.GwtIncompatible; import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL; +import com.sap.sailing.gwt.ui.shared.dispatch.SortedSetResult; import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.RaceCallback; import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; import com.sap.sse.common.impl.MillisecondsTimePoint; @GwtIncompatible final class LiveRaceCalculator implements RaceCallback { - private final LiveRacesDTO result = new LiveRacesDTO(); + private final RaceRefreshCalculator refreshCalculator = new RaceRefreshCalculator(); + private final SortedSetResult result = new SortedSetResult(); @Override public void doForRace(RaceContext rc) { - rc.addLiveRace(result); + result.addValue(rc.getLiveRaceOrNull()); + refreshCalculator.doForRace(rc); } - public ResultWithTTL getResult() { + public ResultWithTTL> getResult() { long ttl = 1000 * 60 * 2; - for(LiveRaceDTO race : result.getRaces()) { + for(LiveRaceDTO race : result.getValues()) { if(race.getViewState() == RaceViewState.RUNNING) { ttl = Math.min(ttl, 1000 * 30); } else if(race.getViewState() == RaceViewState.SCHEDULED) { @@ -27,6 +30,6 @@ final class LiveRaceCalculator implements RaceCallback { ttl = Math.min(ttl, 1000 * 60); } } - return new ResultWithTTL(ttl, result); + return new ResultWithTTL>(refreshCalculator.getTTL(), result); } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceDTO.java index 587f10b0734..76a336dd357 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRaceDTO.java @@ -3,14 +3,12 @@ package com.sap.sailing.gwt.ui.shared.dispatch.event; import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; import com.sap.sailing.gwt.ui.shared.race.RaceProgressDTO; -import com.sap.sailing.gwt.ui.shared.race.SimpleWindDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.SimpleWindDTO; -public class LiveRaceDTO extends RaceMetadataDTO { +public class LiveRaceDTO extends RaceMetadataDTO { private FlagStateDTO flagState; - private SimpleWindDTO wind; - private RaceProgressDTO progress; @SuppressWarnings("unused") @@ -21,14 +19,6 @@ public class LiveRaceDTO extends RaceMetadataDTO { super(regattaName, raceName); } - public SimpleWindDTO getWind() { - return wind; - } - - public void setWind(SimpleWindDTO wind) { - this.wind = wind; - } - public FlagStateDTO getFlagState() { return flagState; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRacesDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRacesDTO.java deleted file mode 100644 index 62b007408b1..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/LiveRacesDTO.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.dispatch.event; - -import java.util.Collection; -import java.util.TreeSet; - -import com.sap.sailing.gwt.ui.shared.dispatch.DTO; - -public class LiveRacesDTO implements DTO { - private TreeSet races = new TreeSet<>(); - - public Collection getRaces() { - return races; - } - - public void addRace(LiveRaceDTO race) { - this.races.add(race); - } - - public boolean hasFleets() { - for(LiveRaceDTO race : races) { - if(race.getFleet() != null) { - return true; - } - } - return false; - } - - public boolean hasCourseAreas() { - for (LiveRaceDTO race : races) { - if (race.getCourseArea() != null) { - return true; - } - } - return false; - } - - public boolean hasCourses() { - for (LiveRaceDTO race : races) { - if (race.getCourse() != null) { - return true; - } - } - return false; - } - - public boolean hasWind() { - for (LiveRaceDTO race : races) { - if (race.getWind() != null) { - return true; - } - } - return false; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java index 1d5ba6f48b9..94c9955ae0e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java @@ -1,7 +1,10 @@ package com.sap.sailing.gwt.ui.shared.dispatch.event; +import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import com.google.gwt.core.shared.GwtIncompatible; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -12,45 +15,57 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.WindFixesFinder; import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState; import com.sap.sailing.domain.abstractlog.race.state.impl.ReadonlyRaceStateImpl; import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.FlagPoleState; -import com.sap.sailing.domain.base.CourseArea; +import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.RaceColumn; +import com.sap.sailing.domain.base.RaceColumnInSeries; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.TimingConstants; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.confidence.BearingWithConfidence; +import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster; +import com.sap.sailing.domain.common.confidence.Weigher; +import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; +import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.racelog.FlagPole; import com.sap.sailing.domain.common.racelog.Flags; import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; -import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; +import com.sap.sailing.domain.leaderboard.ScoreCorrection; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.gwt.server.HomeServiceUtil; import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceTrackingState; import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; import com.sap.sailing.gwt.ui.shared.race.RaceProgressDTO; -import com.sap.sailing.gwt.ui.shared.race.SimpleWindDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.SimpleWindDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.WindStatisticsDTO; +import com.sap.sailing.server.RacingEventService; +import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MediaType; @GwtIncompatible public class RaceContext { - private static final long TIME_BEFORE_START_TO_SHOW_RACES_AS_LIVE = 15 * 60 * 1000; // 15 min + private static final Logger logger = Logger.getLogger(RaceContext.class.getName()); + private static final long TIME_BEFORE_START_TO_SHOW_RACES_AS_LIVE = 60 * 60 * 1000; // 1 hour private static final long TIME_TO_SHOW_CANCELED_RACES_AS_LIVE = 5 * 60 * 1000; // 5 min - private final TimePoint now = MillisecondsTimePoint.now(); private final Leaderboard leaderboard; private final RaceColumn raceColumn; @@ -60,14 +75,17 @@ public class RaceContext { private final RaceLog raceLog; private final ReadonlyRaceState state; private final Event event; - + private final RacingEventService service; private TimePoint startTime; private boolean startTimeCalculated = false; private TimePoint finishTime; private boolean finishTimeCalculated = false; private RaceViewState raceViewState; - - public RaceContext(Event event, Leaderboard leaderboard, RaceColumn raceColumn, Fleet fleet, RaceLogResolver raceLogResolver) { + private List competitors; + + public RaceContext(RacingEventService service, Event event, Leaderboard leaderboard, RaceColumn raceColumn, + Fleet fleet, RaceLogResolver raceLogResolver) { + this.service = service; this.event = event; this.leaderboard = leaderboard; this.raceColumn = raceColumn; @@ -102,34 +120,97 @@ public class RaceContext { if (!isShowFleetData()) { return null; } - return new FleetMetadataDTO(fleet.getName(), fleet.getColor() == null ? null : fleet.getColor().getAsHtml()); + return getFleetMetadata(); + } + + public FleetMetadataDTO getFleetMetadata() { + return new FleetMetadataDTO(fleet.getName(), fleet.getColor() == null ? null : fleet.getColor().getAsHtml(), + fleet.getOrdering()); } private SimpleWindDTO getWindOrNull() { - if(trackedRace != null) { - TimePoint toTimePoint = trackedRace.getEndOfRace() == null ? MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace(); + if (trackedRace != null) { + TimePoint toTimePoint = trackedRace.getEndOfRace() == null ? MillisecondsTimePoint.now().minus( + trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace(); TimePoint newestEvent = trackedRace.getTimePointOfNewestEvent(); - if(newestEvent != null && newestEvent.before(toTimePoint)) { + if (newestEvent != null && newestEvent.before(toTimePoint)) { toTimePoint = newestEvent; } - WindTrack windTrack = trackedRace.getOrCreateWindTrack(new WindSourceImpl(WindSourceType.COMBINED)); - - if(windTrack != null) { - WindWithConfidence> averagedWindWithConfidence = - windTrack.getAveragedWindWithConfidence(trackedRace.getCenterOfCourse(toTimePoint), toTimePoint); - if(averagedWindWithConfidence != null) { + if (windTrack != null) { + WindWithConfidence> averagedWindWithConfidence = windTrack + .getAveragedWindWithConfidence(trackedRace.getCenterOfCourse(toTimePoint), toTimePoint); + if (averagedWindWithConfidence != null) { Wind wind = averagedWindWithConfidence.getObject(); if (wind.getKnots() >= 0.05d) { return new SimpleWindDTO(wind.getFrom().getDegrees(), wind.getKnots()); } } } - } else { - Wind wind = checkForWindFixesFromRaceLog(); - if(wind != null) { - return new SimpleWindDTO(wind.getFrom().getDegrees(), wind.getKnots()); + } + Wind wind = checkForWindFixesFromRaceLog(); + if (wind != null) { + return new SimpleWindDTO(wind.getFrom().getDegrees(), wind.getKnots()); + } + return null; + } + + private WindStatisticsDTO getWindStatisticsOrNull() { + if (trackedRace != null) { + TimePoint toTimePoint = trackedRace.getEndOfRace() == null ? MillisecondsTimePoint.now().minus( + trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace(); + TimePoint newestEvent = trackedRace.getTimePointOfNewestEvent(); + if (newestEvent != null && newestEvent.before(toTimePoint)) { + toTimePoint = newestEvent; } + final BearingWithConfidenceCluster bwcc = new BearingWithConfidenceCluster( + new Weigher() { + private static final long serialVersionUID = -5779398785058438328L; + @Override + public double getConfidence(TimePoint fix, TimePoint request) { + return 1; + } + }); + WindTrack windTrack = trackedRace.getOrCreateWindTrack(new WindSourceImpl(WindSourceType.COMBINED)); + if (windTrack != null) { + TimePoint startTime = getStartTime(); + if (startTime == null) { + startTime = toTimePoint; + } + TimePoint middleOfRace = startTime.plus(startTime.until(toTimePoint).divide(2)); + List pointsToGetWind = Arrays.asList(startTime, middleOfRace, toTimePoint); + Double lowerBoundWindInKnots = null; + Double upperBoundWindInKnots = null; + for (TimePoint timePoint : pointsToGetWind) { + WindWithConfidence> averagedWindWithConfidence = windTrack + .getAveragedWindWithConfidence(trackedRace.getCenterOfCourse(timePoint), timePoint); + if (averagedWindWithConfidence != null) { + Wind wind = averagedWindWithConfidence.getObject(); + + bwcc.add(new BearingWithConfidenceImpl(wind.getBearing(), averagedWindWithConfidence + .getConfidence(), timePoint)); + double currentWindInKnots = wind.getKnots(); + if (currentWindInKnots >= 0.05d) { + if (lowerBoundWindInKnots == null) { + lowerBoundWindInKnots = currentWindInKnots; + upperBoundWindInKnots = currentWindInKnots; + } else { + lowerBoundWindInKnots = Math.min(lowerBoundWindInKnots, currentWindInKnots); + upperBoundWindInKnots = Math.max(upperBoundWindInKnots, currentWindInKnots); + } + } + } + } + if (lowerBoundWindInKnots != null && upperBoundWindInKnots != null) { + BearingWithConfidence average = bwcc.getAverage(middleOfRace); + return new WindStatisticsDTO(average.getObject().reverse().getDegrees(), lowerBoundWindInKnots, + upperBoundWindInKnots); + } + } + } + Wind wind = checkForWindFixesFromRaceLog(); + if (wind != null) { + return new WindStatisticsDTO(wind.getFrom().getDegrees(), wind.getKnots(), wind.getKnots()); } return null; } @@ -138,19 +219,16 @@ public class RaceContext { // Code extracted from SailingServiceImpl.createRaceInfoDTO // TODO: extract to to util to be used from both places TimePoint startTime = state.getStartTime(); - Flags lastUpperFlag = null; Flags lastLowerFlag = null; boolean lastFlagsAreDisplayed = false; boolean lastFlagsDisplayedStateChanged = false; - if (startTime != null) { FlagPoleState activeFlagState = state.getRacingProcedure().getActiveFlags(startTime, now); List activeFlags = activeFlagState.getCurrentState(); FlagPoleState previousFlagState = activeFlagState.getPreviousState(state.getRacingProcedure(), startTime); List previousFlags = previousFlagState.getCurrentState(); FlagPole mostInterestingFlagPole = FlagPoleState.getMostInterestingFlagPole(previousFlags, activeFlags); - // TODO: adapt the LastFlagFinder#getMostRecent method! if (mostInterestingFlagPole != null) { lastUpperFlag = mostInterestingFlagPole.getUpperFlag(); @@ -159,72 +237,48 @@ public class RaceContext { lastFlagsDisplayedStateChanged = previousFlagState.hasPoleChanged(mostInterestingFlagPole); } } - - switch(state.getStatus()) { - case FINISHED: - TimePoint protestStartTime = state.getProtestTime(); - if (protestStartTime != null) { - lastUpperFlag = Flags.BRAVO; - lastLowerFlag = Flags.NONE; - lastFlagsAreDisplayed = true; - lastFlagsDisplayedStateChanged = true; - } - break; - case UNSCHEDULED: - // search for race aborting in last pass - AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog); - RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze(); - if (abortingFlagEvent != null) { - lastUpperFlag = abortingFlagEvent.getUpperFlag(); - lastLowerFlag = abortingFlagEvent.getLowerFlag(); - lastFlagsAreDisplayed = abortingFlagEvent.isDisplayed(); - lastFlagsDisplayedStateChanged = true; - } - break; - case FINISHING: - case RUNNING: - case SCHEDULED: - case STARTPHASE: - case UNKNOWN: - break; - }; - - if(lastUpperFlag != null || lastLowerFlag != null) { + switch (state.getStatus()) { + case FINISHED: + TimePoint protestStartTime = state.getProtestTime(); + if (protestStartTime != null) { + lastUpperFlag = Flags.BRAVO; + lastLowerFlag = Flags.NONE; + lastFlagsAreDisplayed = true; + lastFlagsDisplayedStateChanged = true; + } + break; + case UNSCHEDULED: + // search for race aborting in last pass + AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog); + RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze(); + if (abortingFlagEvent != null) { + lastUpperFlag = abortingFlagEvent.getUpperFlag(); + lastLowerFlag = abortingFlagEvent.getLowerFlag(); + lastFlagsAreDisplayed = abortingFlagEvent.isDisplayed(); + lastFlagsDisplayedStateChanged = true; + } + break; + case FINISHING: + case RUNNING: + case SCHEDULED: + case STARTPHASE: + case UNKNOWN: + break; + } + ; + if (lastUpperFlag != null || lastLowerFlag != null) { return new FlagStateDTO(lastUpperFlag, lastLowerFlag, lastFlagsAreDisplayed, lastFlagsDisplayedStateChanged); } - return null; } - private Regatta getRegatta() { - final Regatta regatta; - if (leaderboard instanceof RegattaLeaderboard) { - regatta = ((RegattaLeaderboard) leaderboard).getRegatta(); - } else { - regatta = null; - } - return regatta; - } - private String getCourseAreaOrNull() { - /** The course area will not be shown if there is only one course area defined for the event */ - if(Util.size(event.getVenue().getCourseAreas()) <= 1) { - return null; - } - CourseArea courseArea = null; - if(leaderboard instanceof FlexibleLeaderboard) { - courseArea = ((FlexibleLeaderboard)leaderboard).getDefaultCourseArea(); - } - Regatta regatta = getRegatta(); - if(regatta != null) { - courseArea = regatta.getDefaultCourseArea(); - } - return courseArea == null ? null : regatta.getDefaultCourseArea().getName(); + return HomeServiceUtil.getCourseAreaNameForRegattaIdThereIsMoreThanOne(event, leaderboard); } private RaceProgressDTO getProgressOrNull() { RaceProgressDTO raceProgress = null; - if(raceDefinition != null && raceDefinition.getCourse() != null && trackedRace != null) { + if (raceDefinition != null && raceDefinition.getCourse() != null && trackedRace != null) { int totalLegsCount = raceDefinition.getCourse().getLegs().size(); int currentLegNo = trackedRace.getLastLegStarted(MillisecondsTimePoint.now()); if (currentLegNo > 0 && totalLegsCount > 0) { @@ -241,154 +295,250 @@ public class RaceContext { } return null; } - + public Date getStartTimeAsDate() { TimePoint startTime = getStartTime(); return startTime == null ? null : startTime.asDate(); } - + public TimePoint getStartTime() { - if(!startTimeCalculated) { - if (trackedRace != null) { - startTime = trackedRace.getStartOfRace(); - } - if (startTime == null && state != null) { + if (!startTimeCalculated) { + if (state != null) { startTime = state.getStartTime(); } + if (startTime == null && trackedRace != null) { + startTime = trackedRace.getStartOfRace(); + } startTimeCalculated = true; } return startTime; } private TimePoint getFinishTime() { - if(!finishTimeCalculated) { - if (trackedRace != null) { - finishTime = trackedRace.getEndOfRace(); - } else if (state != null) { + if (!finishTimeCalculated) { + if (state != null) { finishTime = state.getFinishedTime(); } + if (finishTime == null && trackedRace != null) { + finishTime = trackedRace.getEndOfRace(); + } finishTimeCalculated = true; } return finishTime; } - public void addLiveRace(LiveRacesDTO result) { - LiveRaceDTO liveRaceDTO = getLiveRaceOrNull(); - if(liveRaceDTO != null) { - result.addRace(liveRaceDTO); - } - } - public LiveRaceDTO getLiveRaceOrNull() { // a race is of 'public interest' of a race is a combination of it's 'live' state // and special flags states indicating how the postponed/canceled races will be continued - if(isLiveOrOfPublicInterest()) { + if (isLiveOrOfPublicInterest()) { // the start time is always given for live races LiveRaceDTO liveRaceDTO = new LiveRaceDTO(getRegattaName(), raceColumn.getName()); - liveRaceDTO.setViewState(getLiveRaceViewState()); - liveRaceDTO.setRegattaDisplayName(getRegattaDisplayName()); - liveRaceDTO.setTrackedRaceName(trackedRace != null ? trackedRace.getRaceIdentifier().getRaceName() : null); - liveRaceDTO.setTrackingState(getRaceTrackingState()); - liveRaceDTO.setFleet(getFleetMetadataOrNull()); - liveRaceDTO.setStart(getStartTimeAsDate()); - liveRaceDTO.setBoatClass(HomeServiceUtil.getBoatClassName(leaderboard)); - liveRaceDTO.setCourseArea(getCourseAreaOrNull()); - liveRaceDTO.setCourse(getCourseNameOrNull()); + fillRaceData(liveRaceDTO); liveRaceDTO.setFlagState(getFlagStateOrNull()); liveRaceDTO.setProgress(getProgressOrNull()); liveRaceDTO.setWind(getWindOrNull()); - return liveRaceDTO; } return null; } - + + public RaceListRaceDTO getFinishedRaceOrNull() { + // a race is of 'public interest' of a race is a combination of it's 'live' state + // and special flags states indicating how the postponed/canceled races will be continued + if (getLiveRaceViewState() == RaceViewState.FINISHED) { + // the start time is always given for live races + RaceListRaceDTO liveRaceDTO = new RaceListRaceDTO(getRegattaName(), raceColumn.getName()); + fillRaceData(liveRaceDTO); + liveRaceDTO.setDuration(getDurationOrNull()); + liveRaceDTO.setWinner(getWinnerOrNull()); + liveRaceDTO.setWindSourcesCount(getWindSourceCount()); + liveRaceDTO.setVideoCount(getVideoCount()); + liveRaceDTO.setAudioCount(getAudioCount()); + liveRaceDTO.setWind(getWindStatisticsOrNull()); + return liveRaceDTO; + } + return null; + } + + private int getAudioCount() { + return getMediaCount(MediaType.audio); + } + + private int getVideoCount() { + return getMediaCount(MediaType.video); + } + + private int getMediaCount(MediaType mediaType) { + int mediaCount = 0; + if (trackedRace != null) { + for (MediaTrack mediaTrack : service.getMediaTracksForRace(trackedRace.getRaceIdentifier())) { + if (mediaTrack.mimeType != null && mediaTrack.mimeType.mediaType == mediaType) { + mediaCount++; + } + } + } + return mediaCount; + } + + private int getWindSourceCount() { + if (trackedRace != null) { + return Util.size(trackedRace.getWindSources(WindSourceType.EXPEDITION)); + } + return 0; + } + + private SimpleCompetitorDTO getWinnerOrNull() { + if (getLiveRaceViewState() != RaceViewState.FINISHED) { + // We can't reliably calculate the winner for non finished races + return null; + } + // TODO do not calculate the winner if the blue flag is currently shown. + try { + TimePoint finishTime = getFinishTime(); + if(finishTime == null) { + finishTime = HomeServiceUtil.getLiveTimePoint(); + } + competitors = leaderboard.getCompetitorsFromBestToWorst(raceColumn, finishTime); + if (competitors == null || competitors.isEmpty()) { + return null; + } + if (Util.size(raceColumn.getFleets()) == 1) { + return new SimpleCompetitorDTO(competitors.get(0)); + } + if (trackedRace == null) { + return null; + } + for (Competitor competitor : competitors) { + Fleet fleetOfCompetitor = raceColumn.getFleetOfCompetitor(competitor); + if (fleetOfCompetitor == null) { + continue; + } + if (Util.equalsWithNull(fleet.getName(), fleetOfCompetitor.getName())) { + return new SimpleCompetitorDTO(competitor); + } + } + } catch (NoWindException e) { + logger.log(Level.WARNING, "Error while calculating winner for race.", e); + } + return null; + } + + private Duration getDurationOrNull() { + TimePoint startTime = getStartTime(); + TimePoint finishTime = getFinishTime(); + if (startTime != null && finishTime != null) { + return startTime.until(finishTime); + } + return null; + } + + private void fillRaceData(RaceMetadataDTO dto) { + dto.setViewState(getLiveRaceViewState()); + dto.setRegattaDisplayName(getRegattaDisplayName()); + dto.setTrackedRaceName(trackedRace != null ? trackedRace.getRaceIdentifier().getRaceName() : null); + dto.setTrackingState(getRaceTrackingState()); + dto.setFleet(getFleetMetadataOrNull()); + dto.setStart(getStartTimeAsDate()); + dto.setBoatClass(HomeServiceUtil.getBoatClassName(leaderboard)); + dto.setCourseArea(getCourseAreaOrNull()); + dto.setCourse(getCourseNameOrNull()); + } + public boolean isLiveOrOfPublicInterest() { TimePoint startTime = getStartTime(); boolean result = false; - if(startTime != null) { - if(trackedRace != null && trackedRace.hasGPSData() && trackedRace.hasWindData()) { + if (startTime != null) { + if (trackedRace != null && trackedRace.hasGPSData() && trackedRace.hasWindData()) { result = trackedRace.isLive(now); } else { TimePoint finishTime = getFinishTime(); // no data from tracking but maybe a manual setting of the start and finish time TimePoint startOfLivePeriod = startTime.minus(TIME_BEFORE_START_TO_SHOW_RACES_AS_LIVE); - TimePoint endOfLivePeriod = finishTime != null ? finishTime.plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS) : null; - if(now.after(startOfLivePeriod) && (endOfLivePeriod == null || now.before(endOfLivePeriod))) { + TimePoint endOfLivePeriod = finishTime != null ? finishTime + .plus(TimingConstants.IS_LIVE_GRACE_PERIOD_IN_MILLIS) : null; + if (now.after(startOfLivePeriod) && (endOfLivePeriod == null || now.before(endOfLivePeriod))) { result = true; } } - } else if (raceLog != null) { // in case there is not start time set it could be an postponed or abandoned race RaceLogFlagEvent abortingFlagEvent = checkForAbortFlagEvent(); - if(abortingFlagEvent != null) { + if (abortingFlagEvent != null) { TimePoint abortingTimeInPassBefore = abortingFlagEvent.getLogicalTimePoint(); if (now.minus(abortingTimeInPassBefore.asMillis()).asMillis() < TIME_TO_SHOW_CANCELED_RACES_AS_LIVE) { result = true; // TODO: Problem: This causes the race added to the live races list without having a start time!!! - // This does not work right now -> consider using a start time of the last pass. + // This does not work right now -> consider using a start time of the last pass. } } } - return result; } public RaceTrackingState getRaceTrackingState() { RaceTrackingState trackingState = RaceTrackingState.NOT_TRACKED; - if(trackedRace != null) { + if (trackedRace != null) { trackingState = RaceTrackingState.TRACKED_NO_VALID_DATA; - if(trackedRace.hasWindData() && trackedRace.hasGPSData()) { + if (trackedRace.hasWindData() && trackedRace.hasGPSData()) { trackingState = RaceTrackingState.TRACKED_VALID_DATA; } } return trackingState; } - - private RaceViewState getLiveRaceViewState() { - if(raceViewState == null) { - raceViewState = calculateLiveRaceViewState(); + + public RaceViewState getLiveRaceViewState() { + if (raceViewState == null) { + raceViewState = calculateRaceViewState(); } return raceViewState; } - - private RaceViewState calculateLiveRaceViewState() { + + private RaceViewState calculateRaceViewState() { TimePoint startTime = getStartTime(); TimePoint finishTime = getFinishTime(); - RaceViewState raceState = RaceViewState.RUNNING; if (startTime != null && now.before(startTime)) { - raceState = RaceViewState.SCHEDULED; - } else if (finishTime != null && now.after(finishTime)) { - raceState = RaceViewState.FINISHED; - } else if(raceLog != null) { + return RaceViewState.SCHEDULED; + } + if (finishTime != null && now.after(finishTime)) { + return RaceViewState.FINISHED; + } + if(raceLog != null) { RaceLogFlagEvent abortingFlagEvent = checkForAbortFlagEvent(); if (abortingFlagEvent != null) { Flags upperFlag = abortingFlagEvent.getUpperFlag(); if(upperFlag.equals(Flags.AP)) { - raceState = RaceViewState.POSTPONED; - } else if(upperFlag.equals(Flags.NOVEMBER)) { - raceState = RaceViewState.ABANDONED; - } else if(upperFlag.equals(Flags.FIRSTSUBSTITUTE)) { - raceState = RaceViewState.ABANDONED; + return RaceViewState.POSTPONED; + } + if(upperFlag.equals(Flags.NOVEMBER)) { + return RaceViewState.ABANDONED; + } + if(upperFlag.equals(Flags.FIRSTSUBSTITUTE)) { + return RaceViewState.ABANDONED; } } } - return raceState; + ScoreCorrection scoreCorrection = leaderboard.getScoreCorrection(); + if(trackedRace == null && scoreCorrection != null && scoreCorrection.hasCorrectionForNonTrackedFleet(raceColumn)) { + return RaceViewState.FINISHED; + } + if(startTime != null) { + return RaceViewState.RUNNING; + } + return RaceViewState.PLANNED; } - + private Wind checkForWindFixesFromRaceLog() { WindFixesFinder windFixesFinder = new WindFixesFinder(raceLog); List windList = windFixesFinder.analyze(); - if(windList.size() > 0) { - return windList.get(windList.size()-1); + if (windList.size() > 0) { + return windList.get(windList.size() - 1); } return null; } - + private RaceLogFlagEvent checkForAbortFlagEvent() { RaceLogFlagEvent result = null; - if(raceLog != null) { + if (raceLog != null) { AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog); RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze(); if (abortingFlagEvent != null) { @@ -409,4 +559,27 @@ public class RaceContext { public RegattaAndRaceIdentifier getRaceIdentifier() { return trackedRace.getRaceIdentifier(); } + + public String getSeriesName() { + if (raceColumn instanceof RaceColumnInSeries) { + return ((RaceColumnInSeries) raceColumn).getSeries().getName(); + } + return ""; + } + + public String getRaceName() { + return raceColumn.getName(); + } + + public String getFleetName() { + return fleet.getName(); + } + + public boolean isFinished() { + return getLiveRaceViewState() == RaceViewState.FINISHED; + } + + public boolean isLive() { + return getLiveRaceViewState() == RaceViewState.RUNNING; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListDataCalculator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListDataCalculator.java new file mode 100644 index 00000000000..e305a16ffde --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListDataCalculator.java @@ -0,0 +1,25 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import java.util.Collection; +import java.util.TreeSet; + +import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.RaceCallback; + +@GwtIncompatible +public class RaceListDataCalculator implements RaceCallback { + private final TreeSet races = new TreeSet<>(); + + @Override + public void doForRace(RaceContext context) { + RaceListRaceDTO finishedRace = context.getFinishedRaceOrNull(); + if(finishedRace != null) { + races.add(finishedRace); + } + } + + public Collection getResult() { + return races; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListDayDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListFleetDTO.java similarity index 50% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListDayDTO.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListFleetDTO.java index 6a8125f72e4..89c205b4742 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListDayDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListFleetDTO.java @@ -1,23 +1,23 @@ package com.sap.sailing.gwt.ui.shared.dispatch.event; import java.util.ArrayList; -import java.util.Date; import java.util.List; import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; -public class RaceListDayDTO implements IsSerializable { - - private Date day; +public class RaceListFleetDTO implements IsSerializable { + private FleetMetadataDTO fleet; + private int competitorCount = (int) (Math.random() * 10); // TODO private ArrayList races = new ArrayList<>(); @SuppressWarnings("unused") - private RaceListDayDTO() { + private RaceListFleetDTO() { } - public RaceListDayDTO(Date day) { - this.day = day; + public RaceListFleetDTO(FleetMetadataDTO fleet) { + this.fleet = fleet; } public void addRace(RaceListRaceDTO race) { @@ -25,8 +25,12 @@ public class RaceListDayDTO implements IsSerializable { races.add(race); } - public Date getDay() { - return day; + public FleetMetadataDTO getFleet() { + return fleet; + } + + public int getCompetitorCount() { + return competitorCount; } public List getRaces() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListRaceDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListRaceDTO.java index 9270e530aad..3c1306abff2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListRaceDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListRaceDTO.java @@ -1,15 +1,17 @@ package com.sap.sailing.gwt.ui.shared.dispatch.event; -import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO; -import com.sap.sailing.gwt.ui.shared.race.WindStatisticsDTO; +import com.sap.sailing.gwt.ui.shared.race.wind.WindStatisticsDTO; +import com.sap.sse.common.Duration; -public class RaceListRaceDTO extends RaceMetadataDTO { - - private FlagStateDTO flagState; - - private WindStatisticsDTO wind; +public class RaceListRaceDTO extends RaceMetadataDTO { + private Duration duration; + private SimpleCompetitorDTO winner; + private int windSourcesCount; + private int videoCount; + private int audioCount; + @SuppressWarnings("unused") private RaceListRaceDTO() { } @@ -18,19 +20,34 @@ public class RaceListRaceDTO extends RaceMetadataDTO { super(regattaName, raceName); } - public WindStatisticsDTO getWind() { - return wind; + public Duration getDuration() { + return duration; } - - public void setWind(WindStatisticsDTO wind) { - this.wind = wind; + public void setDuration(Duration duration) { + this.duration = duration; } - - public FlagStateDTO getFlagState() { - return flagState; + public SimpleCompetitorDTO getWinner() { + return winner; } - - public void setFlagState(FlagStateDTO flagState) { - this.flagState = flagState; + public void setWinner(SimpleCompetitorDTO winner) { + this.winner = winner; + } + public int getWindSourcesCount() { + return windSourcesCount; + } + public void setWindSourcesCount(int windFixesCount) { + this.windSourcesCount = windFixesCount; + } + public int getVideoCount() { + return videoCount; + } + public void setVideoCount(int videoCount) { + this.videoCount = videoCount; + } + public int getAudioCount() { + return audioCount; + } + public void setAudioCount(int audioCount) { + this.audioCount = audioCount; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListSeriesDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListSeriesDTO.java new file mode 100644 index 00000000000..12d1c0e1f8a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListSeriesDTO.java @@ -0,0 +1,57 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; + +public class RaceListSeriesDTO implements IsSerializable { + + private String seriesName; + private int raceCount = 0; + private int competitorCount = (int) (Math.random() * 10); // TODO + private Map fleets = new HashMap<>(); + + @SuppressWarnings("unused") + private RaceListSeriesDTO() { + } + + public RaceListSeriesDTO(String seriesName) { + this.seriesName = seriesName; + } + + public void addRace(RaceListRaceDTO race) { + RaceListFleetDTO fleet = ensureFleet(race.getFleet()); + fleet.addRace(race); + raceCount++; + } + + private RaceListFleetDTO ensureFleet(FleetMetadataDTO fleetData) { + String fleetName = fleetData == null ? null : fleetData.getFleetName(); + RaceListFleetDTO fleet = fleets.get(fleetName); + if (fleet == null) { + fleets.put(fleetName, fleet = new RaceListFleetDTO(fleetData)); + } + return fleet; + } + + public String getSeriesName() { + return seriesName; + } + + public int getRaceCount() { + return raceCount; + } + + public int getCompetitorCount() { + return competitorCount; + } + + public Collection getFleets() { + return fleets.values(); + } + + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListViewDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListViewDTO.java deleted file mode 100644 index 225f99a66fa..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceListViewDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.dispatch.event; - -import java.util.ArrayList; -import java.util.List; - -import com.sap.sailing.gwt.ui.shared.dispatch.DTO; - -public class RaceListViewDTO implements DTO { - - private LiveRacesDTO liveRaces; - - private ArrayList raceDays = new ArrayList<>(); - - public void addRace(RaceListRaceDTO race) { - // TODO implement based on race.getStart(); - } - - public LiveRacesDTO getLiveRaces() { - return liveRaces; - } - - public List getRaceDays() { - return raceDays; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceRefreshCalculator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceRefreshCalculator.java new file mode 100644 index 00000000000..35a845fe1cc --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceRefreshCalculator.java @@ -0,0 +1,28 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import com.google.gwt.core.shared.GwtIncompatible; +import com.sap.sailing.gwt.ui.shared.dispatch.event.EventActionUtil.RaceCallback; +import com.sap.sailing.gwt.ui.shared.race.RaceMetadataDTO.RaceViewState; +import com.sap.sse.common.impl.MillisecondsTimePoint; + +@GwtIncompatible +final class RaceRefreshCalculator implements RaceCallback { + private long ttl = 1000 * 60 * 3; + + @Override + public void doForRace(RaceContext rc) { + RaceViewState state = rc.getLiveRaceViewState(); + if(state == RaceViewState.RUNNING) { + ttl = Math.min(ttl, 1000 * 30); + } else if(state == RaceViewState.SCHEDULED) { + int timeTillRace = (int) MillisecondsTimePoint.now().until(rc.getStartTime()).asMillis(); + ttl = Math.min(ttl, Math.min(1000 * 60, timeTillRace)); + } else if(state == RaceViewState.POSTPONED || state == RaceViewState.ABANDONED) { + ttl = Math.min(ttl, 1000 * 60); + } + } + + public long getTTL() { + return ttl; + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RegattaProgressCalculator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RegattaProgressCalculator.java new file mode 100644 index 00000000000..6515b2bac73 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RegattaProgressCalculator.java @@ -0,0 +1,82 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.event; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressFleetDTO; +import com.sap.sailing.gwt.ui.shared.dispatch.regatta.RegattaProgressSeriesDTO; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; + +public class RegattaProgressCalculator implements EventActionUtil.RaceCallback { + private final Map> racesPerSeries = new LinkedHashMap<>(); + private final Map>> racesPerSeriesAndFleet = new HashMap<>(); + private final Map>> liveRacesPerSeriesAndFleet = new HashMap<>(); + private final Map fleets = new HashMap<>(); + + @Override + public void doForRace(RaceContext context) { + String seriesName = context.getSeriesName(); + + Set racesInSeries = racesPerSeries.get(seriesName); + if(racesInSeries == null) { + racesInSeries = new HashSet<>(); + racesPerSeries.put(seriesName, racesInSeries); + } + String raceName = context.getRaceName(); + racesInSeries.add(raceName); + + Map> racesInSeriesAndFleet = racesPerSeriesAndFleet.get(seriesName); + if(racesInSeriesAndFleet == null) { + racesInSeriesAndFleet = new LinkedHashMap>(); + racesPerSeriesAndFleet.put(seriesName, racesInSeriesAndFleet); + } + String fleetName = context.getFleetName(); + Set racesInFleet = racesInSeriesAndFleet.get(fleetName); + if(racesInFleet == null) { + racesInFleet = new HashSet<>(); + racesInSeriesAndFleet.put(fleetName, racesInFleet); + } + Map> liveRacesInSeriesAndFleet = liveRacesPerSeriesAndFleet.get(seriesName); + if(liveRacesInSeriesAndFleet == null) { + liveRacesInSeriesAndFleet = new LinkedHashMap>(); + liveRacesPerSeriesAndFleet.put(seriesName, liveRacesInSeriesAndFleet); + } + Set liveRacesInFleet = liveRacesInSeriesAndFleet.get(fleetName); + if(liveRacesInFleet == null) { + liveRacesInFleet = new HashSet<>(); + liveRacesInSeriesAndFleet.put(fleetName, liveRacesInFleet); + } + if(context.isFinished()) { + racesInFleet.add(raceName); + } + if(context.isLive()) { + liveRacesInFleet.add(raceName); + } + + if(!fleets.containsKey(fleetName)) { + fleets.put(fleetName, context.getFleetMetadata()); + } + } + + public RegattaProgressDTO getResult() { + RegattaProgressDTO progress = new RegattaProgressDTO(); + for(Map.Entry> racesInSeries : racesPerSeries.entrySet()) { + String seriesName = racesInSeries.getKey(); + int totalRaceCount = racesInSeries.getValue().size(); + RegattaProgressSeriesDTO regattaProgressOfSeries = new RegattaProgressSeriesDTO(seriesName, totalRaceCount); + for(Map.Entry> racesInFleet : racesPerSeriesAndFleet.get(seriesName).entrySet()) { + String fleetName = racesInFleet.getKey(); + int finishedRacesForFleet = racesInFleet.getValue().size(); + int liveRacesForFleet = liveRacesPerSeriesAndFleet.get(seriesName).get(fleetName).size(); + regattaProgressOfSeries.addFleet(fleets.get(fleetName), new RegattaProgressFleetDTO(finishedRacesForFleet, liveRacesForFleet)); + } + progress.addSeries(regattaProgressOfSeries); + } + return progress; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressDTO.java new file mode 100644 index 00000000000..5c7885fba0b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressDTO.java @@ -0,0 +1,18 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.regatta; + +import java.util.ArrayList; +import java.util.List; + +import com.sap.sailing.gwt.ui.shared.dispatch.DTO; + +public class RegattaProgressDTO implements DTO { + private ArrayList series = new ArrayList<>(); + + public void addSeries(RegattaProgressSeriesDTO regattaProgressOfSeries) { + series.add(regattaProgressOfSeries); + } + + public List getSeries() { + return series; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressFleetDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressFleetDTO.java new file mode 100644 index 00000000000..be3a530de8e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressFleetDTO.java @@ -0,0 +1,29 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.regatta; + +import com.sap.sailing.gwt.ui.shared.dispatch.DTO; + +public class RegattaProgressFleetDTO implements DTO { + private int finishedRaceCount; + private int liveRaceCount; + + @SuppressWarnings("unused") + private RegattaProgressFleetDTO() { + } + + public RegattaProgressFleetDTO(int finishedRaceCount, int liveRaceCount) { + this.finishedRaceCount = finishedRaceCount; + this.liveRaceCount = liveRaceCount; + } + + public int getFinishedRaceCount() { + return finishedRaceCount; + } + + public int getLiveRaceCount() { + return liveRaceCount; + } + + public int getFinishedAndLiveRaceCount() { + return finishedRaceCount + liveRaceCount; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressSeriesDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressSeriesDTO.java new file mode 100644 index 00000000000..aa4fa6f57a2 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaProgressSeriesDTO.java @@ -0,0 +1,55 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.regatta; + +import java.util.Map; +import java.util.TreeMap; + +import com.sap.sailing.gwt.ui.shared.dispatch.DTO; +import com.sap.sailing.gwt.ui.shared.race.FleetMetadataDTO; + +public class RegattaProgressSeriesDTO implements DTO { + private String name; + private int totalRaceCount; + private TreeMap fleetState = new TreeMap<>(); + + @SuppressWarnings("unused") + private RegattaProgressSeriesDTO() { + } + + public RegattaProgressSeriesDTO(String name, int totalRaceCount) { + this.name = name; + this.totalRaceCount = totalRaceCount; + } + + public String getName() { + return name; + } + + public int getTotalRaceCount() { + return totalRaceCount; + } + + public Map getFleetState() { + return fleetState; + } + + public void addFleet(FleetMetadataDTO fleet, RegattaProgressFleetDTO stats) { + fleetState.put(fleet, stats); + } + + public boolean isCompleted() { + for (RegattaProgressFleetDTO stats : fleetState.values()) { + if (stats.getFinishedRaceCount() < totalRaceCount) { + return false; + } + } + return !fleetState.isEmpty(); + } + + public int getProgressRaceCount() { + int progressRaceCount = 0; + for (RegattaProgressFleetDTO stats : fleetState.values()) { + progressRaceCount = Math.max(progressRaceCount, stats.getFinishedRaceCount()); + } + return progressRaceCount; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaWithProgressDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaWithProgressDTO.java new file mode 100644 index 00000000000..a245f407350 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/regatta/RegattaWithProgressDTO.java @@ -0,0 +1,23 @@ +package com.sap.sailing.gwt.ui.shared.dispatch.regatta; + +import com.sap.sailing.gwt.ui.shared.eventview.RegattaMetadataDTO; + +public class RegattaWithProgressDTO extends RegattaMetadataDTO { + private RegattaProgressDTO progress; + + public RegattaWithProgressDTO() { + } + + public RegattaWithProgressDTO(RegattaProgressDTO progress) { + super(); + this.progress = progress; + } + + public RegattaProgressDTO getProgress() { + return progress; + } + + public void setProgress(RegattaProgressDTO progress) { + this.progress = progress; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/HasRegattaMetadata.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/HasRegattaMetadata.java index 7c849454698..ca95f5abe4d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/HasRegattaMetadata.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/HasRegattaMetadata.java @@ -37,4 +37,6 @@ public interface HasRegattaMetadata { Date getEndDate(); RegattaState getState(); + + String getDefaultCourseAreaName(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/RegattaMetadataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/RegattaMetadataDTO.java index 3b228202187..cd66c98c588 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/RegattaMetadataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/eventview/RegattaMetadataDTO.java @@ -9,6 +9,7 @@ public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegatt private int trackedRacesCount; private String boatClass; private String boatCategory; + private String defaultCourseAreaName; private Date startDate; private Date endDate; private RegattaState state; @@ -91,4 +92,12 @@ public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegatt public void setState(RegattaState state) { this.state = state; } + + public String getDefaultCourseAreaName() { + return defaultCourseAreaName; + } + + public void setDefaultCourseAreaName(String defaultCourseAreaName) { + this.defaultCourseAreaName = defaultCourseAreaName; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/FleetMetadataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/FleetMetadataDTO.java index 9cf9fc546b7..6f5bd7cb243 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/FleetMetadataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/FleetMetadataDTO.java @@ -4,17 +4,20 @@ import com.google.gwt.user.client.rpc.IsSerializable; public class FleetMetadataDTO implements IsSerializable, Comparable { + private static final String DEFAULT_FLEET_COLOR = "#f0ab00"; private String fleetName; private String fleetColor; + private int ordering; @SuppressWarnings("unused") private FleetMetadataDTO() { } - public FleetMetadataDTO(String fleetName, String fleetColor) { + public FleetMetadataDTO(String fleetName, String fleetColor, int ordering) { super(); this.fleetName = fleetName; this.fleetColor = fleetColor; + this.ordering = ordering; } public String getFleetName() { @@ -22,11 +25,18 @@ public class FleetMetadataDTO implements IsSerializable, Comparable { +public abstract class RaceMetadataDTO implements DTO, Comparable> { public enum RaceViewState { PLANNED { // no start time set @@ -66,6 +67,7 @@ public class RaceMetadataDTO implements IsSerializable, Comparable o) { Date thisStart = getStart(); Date otherStart = o.getStart(); if(Util.equalsWithNull(thisStart, otherStart)) { @@ -172,7 +182,7 @@ public class RaceMetadataDTO implements IsSerializable, Comparable o) { String thisRegattaName = getRegattaName(); String otherRegattaName = o.getRegattaName(); if(thisRegattaName != otherRegattaName) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/SimpleWindDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/SimpleWindDTO.java deleted file mode 100644 index 6a23dd30aa2..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/SimpleWindDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.race; - -import com.google.gwt.user.client.rpc.IsSerializable; - -public class SimpleWindDTO implements IsSerializable { - private Double trueWindFromDeg; - private Double trueWindSpeedInKnots; - - @SuppressWarnings("unused") - private SimpleWindDTO() { - } - - public SimpleWindDTO(Double trueWindFromDeg, Double trueWindSpeedInKnots) { - super(); - this.trueWindFromDeg = trueWindFromDeg; - this.trueWindSpeedInKnots = trueWindSpeedInKnots; - } - - public Double getTrueWindFromDeg() { - return trueWindFromDeg; - } - - public Double getTrueWindSpeedInKnots() { - return trueWindSpeedInKnots; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/WindStatisticsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/WindStatisticsDTO.java deleted file mode 100644 index 91c4aea64fd..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/WindStatisticsDTO.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.race; - -public class WindStatisticsDTO extends SimpleWindDTO { - - public WindStatisticsDTO(Double trueWindFromDeg, Double trueWindSpeedInKnots) { - super(trueWindFromDeg, trueWindSpeedInKnots); - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/AbstractWindDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/AbstractWindDTO.java new file mode 100644 index 00000000000..e372809709d --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/AbstractWindDTO.java @@ -0,0 +1,19 @@ +package com.sap.sailing.gwt.ui.shared.race.wind; + +import com.google.gwt.user.client.rpc.IsSerializable; + +public abstract class AbstractWindDTO implements IsSerializable { + + private Double trueWindFromDeg; + + protected AbstractWindDTO() { + } + + protected AbstractWindDTO(Double trueWindFromDeg) { + this.trueWindFromDeg = trueWindFromDeg; + } + + public Double getTrueWindFromDeg() { + return trueWindFromDeg; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/SimpleWindDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/SimpleWindDTO.java new file mode 100644 index 00000000000..c5435c8b3be --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/SimpleWindDTO.java @@ -0,0 +1,19 @@ +package com.sap.sailing.gwt.ui.shared.race.wind; + + +public class SimpleWindDTO extends AbstractWindDTO { + private Double trueWindSpeedInKnots; + + protected SimpleWindDTO() { + super(); + } + + public SimpleWindDTO(Double trueWindFromDeg, Double trueWindSpeedInKnots) { + super(trueWindFromDeg); + this.trueWindSpeedInKnots = trueWindSpeedInKnots; + } + + public Double getTrueWindSpeedInKnots() { + return trueWindSpeedInKnots; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/WindStatisticsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/WindStatisticsDTO.java new file mode 100644 index 00000000000..ab5ff2d2614 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/race/wind/WindStatisticsDTO.java @@ -0,0 +1,30 @@ +package com.sap.sailing.gwt.ui.shared.race.wind; + +public class WindStatisticsDTO extends AbstractWindDTO { + + private Double trueLowerboundWindInKnots; + private Double trueUpperboundWindInKnots; + + protected WindStatisticsDTO() { + super(); + } + + public WindStatisticsDTO(Double trueWindFromDeg, Double trueLowerboundWindInKnots, Double trueUpperboundWindInKnots) { + super(trueWindFromDeg); + this.trueLowerboundWindInKnots = trueLowerboundWindInKnots; + this.trueUpperboundWindInKnots = trueUpperboundWindInKnots; + } + + public Double getTrueLowerboundWindInKnots() { + return trueLowerboundWindInKnots; + } + + public Double getTrueUpperboundWindInKnots() { + return trueUpperboundWindInKnots; + } + + public Double getRoundedDifference() { + return (Math.round(trueUpperboundWindInKnots*10d)/10d) - (Math.round(trueLowerboundWindInKnots*10d)/10d); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java index b9865de34c4..5128e0248f2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorEntryPoint.java @@ -16,6 +16,7 @@ import com.sap.sailing.gwt.ui.client.SimulatorService; import com.sap.sailing.gwt.ui.client.SimulatorServiceAsync; import com.sap.sailing.simulator.util.SailingSimulatorConstants; import com.sap.sse.gwt.client.EntryPointHelper; +import com.sap.sse.gwt.resources.Highcharts; public class SimulatorEntryPoint extends AbstractSailingEntryPoint { @@ -45,6 +46,7 @@ public class SimulatorEntryPoint extends AbstractSailingEntryPoint { @Override protected void doOnModuleLoad() { + Highcharts.ensureInjectedWithExport(); super.doOnModuleLoad(); EntryPointHelper.registerASyncService((ServiceDefTarget) simulatorService, RemoteServiceMappingConstants.simulatorServiceRemotePath); checkUrlParameters(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java index 7fb1a5cc601..b32bf914d2d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/SimulatorMap.java @@ -360,7 +360,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ this.replayPathCanvasOverlays = null; this.raceCourseCanvasOverlay = null; this.timeListeners = new LinkedList(); - this.initializeData(showMapControls); + this.initializeData(showMapControls, /* showHeaderPanel */ true); this.parent = parent; } @@ -1000,7 +1000,7 @@ public class SimulatorMap extends AbsolutePanel implements RequiresDataInitializ } @Override - public void initializeData(boolean showMapControls) { + public void initializeData(boolean showMapControls, boolean showHeaderPanel) { loadMapsAPIV3(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml index e4e99b518f1..6f0e1c83121 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/DataMining.gwt.xml @@ -4,7 +4,7 @@ - + @@ -24,7 +24,6 @@ - @@ -43,5 +42,6 @@ + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/EmbeddedMapAndWindChart.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/EmbeddedMapAndWindChart.gwt.xml new file mode 100644 index 00000000000..8085c4deb08 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/EmbeddedMapAndWindChart.gwt.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/PolarSheets.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/PolarSheets.gwt.xml index 097de8c561b..a4d8c7aa077 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/PolarSheets.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/PolarSheets.gwt.xml @@ -6,11 +6,10 @@ - - + @@ -38,4 +37,6 @@ + + \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml index cbdc948f6c7..7fe26ecafdf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Simulator.gwt.xml @@ -13,7 +13,7 @@ - + @@ -23,8 +23,6 @@ - - @@ -44,4 +42,8 @@ It generates a set of files in WEB-INF/deploy; those files need to be placed on the server --> + + + + \ No newline at end of file diff --git a/java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Manage2SailEventResultsParserImpl.java b/java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Manage2SailEventResultsParserImpl.java index f5c0a9f1b80..00dc355df69 100644 --- a/java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Manage2SailEventResultsParserImpl.java +++ b/java/com.sap.sailing.manage2sail/src/com/sap/sailing/manage2sail/Manage2SailEventResultsParserImpl.java @@ -43,7 +43,7 @@ public class Manage2SailEventResultsParserImpl implements Manage2SailEventResult result.setName((String) jsonRoot.get("Name")); result.setXrrUrl(parseURL(jsonRoot, "XrrUrl")); JSONObject jsonTracking = (JSONObject) jsonRoot.get("Tracking"); - if(jsonTracking != null) { + if (jsonTracking != null) { result.setTrackingDataHost((String) jsonTracking.get("Host")); result.setTrackingDataPort(parseInteger(jsonTracking, "Port")); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow360.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow360.java index 52dea1533a1..50b63d9d7c5 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow360.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow360.java @@ -827,7 +827,7 @@ public class PathGeneratorTreeGrow360 extends PathGeneratorBase { // check for time-out if (this.isTimedOut()) { - //reachedEnd = true; + reachedEnd = true; } } // main while-loop diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java index ad170127b1c..442633e8388 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java @@ -1,11 +1,14 @@ package com.sap.sailing.simulator.windfield.impl; +import java.util.concurrent.ConcurrentHashMap; + import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.simulator.TimedPosition; +import com.sap.sailing.simulator.impl.TimedPositionImpl; import com.sap.sailing.simulator.windfield.WindFieldGenerator; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; @@ -21,13 +24,22 @@ import com.sap.sse.common.impl.MillisecondsTimePoint; public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements WindFieldGenerator { private static final long serialVersionUID = -7005970781594631010L; - private static final double EPSILON_DISTANCE_METER = 20; - private static final long EPSILON_TIME_MILLIS = 5000; + private static final double EPSILON_DISTANCE_METER = 20; + private static final long EPSILON_TIME_MILLIS = 5000; private TrackedRace race; + private final ConcurrentHashMap cache; + private TimePoint startSimulationTime = null; + public WindFieldTrackedRaceImpl(TrackedRace race) { super(null, null); this.race = race; + this.cache = new ConcurrentHashMap<>(); + if (race.isLive(MillisecondsTimePoint.now())) { + startSimulationTime = MillisecondsTimePoint.now(); + } else { + startSimulationTime = null; + } } @Override @@ -38,10 +50,18 @@ public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements double epsLng = epsLat / Math.cos(qLat * Math.PI / 180.0); double qLng = Math.floor(pos.getLngDeg() / epsLng) * epsLng; TimePoint time = timedPosition.getTimePoint(); + if ((startSimulationTime != null) && (time.after(startSimulationTime))) { + time = startSimulationTime; + } long epsTime = EPSILON_TIME_MILLIS; TimePoint qTime = new MillisecondsTimePoint((long) (Math.floor(time.asMillis() / epsTime) * epsTime)); Position qPosition = new DegreePosition(qLat, qLng); - Wind wind = this.race.getWind(qPosition, qTime); + TimedPosition qTimedPosition = new TimedPositionImpl(qTime, qPosition); + Wind wind = cache.get(qTimedPosition); + if (wind == null) { + wind = this.race.getWind(qPosition, qTime); + cache.put(qTimedPosition, wind); + } return wind; } diff --git a/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css b/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css index 31e30169820..4f765a519e3 100644 --- a/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css +++ b/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css @@ -30,11 +30,9 @@ select { cursor: pointer; cursor: hand; font-size:small; - background: url("images/hborder.png") repeat-x 0px -2077px; border:1px solid #bbb; border-bottom: 1px solid #a0a0a0; border-radius: 3px; - -moz-border-radius: 3px; } .gwt-Button:active { border: 1px inset #ccc; diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index 86e78215568..60fe96a9e8b 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -34,8 +34,11 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; @@ -46,6 +49,8 @@ import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindPositionMode; +import com.sap.sailing.domain.tracking.WindTrack; +import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.server.RacingEventService; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; @@ -97,7 +102,7 @@ public class LeaderboardData extends ExportAction { * * */ - private Element createLeaderboardXML(Leaderboard leaderboard, List competitors, List races, Util.Pair> leaderboardConfidenceAndErrorMessages) { + private Element createLeaderboardXML(Leaderboard leaderboard, List windData, List competitors, List races, Util.Pair> leaderboardConfidenceAndErrorMessages) { Element leaderboardElement = new Element("leaderboard"); addNamedElementWithValue(leaderboardElement, "name", leaderboard.getName()); addNamedElementWithValue(leaderboardElement, "display_name", leaderboard.getDisplayName()); @@ -106,6 +111,9 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(leaderboardElement, "boat_class", getBoatClassName(leaderboard)); leaderboardElement.addContent(createTimedXML("last_modification_", leaderboard.getTimePointOfLatestModification())); leaderboardElement.addContent(createDataConfidenceXML(leaderboardConfidenceAndErrorMessages)); + Element windContainer = new Element("wind"); + windContainer.addContent(windData); + leaderboardElement.addContent(windContainer); leaderboardElement.addContent(competitors); leaderboardElement.addContent(races); return leaderboardElement; @@ -202,6 +210,35 @@ public class LeaderboardData extends ExportAction { return windElements; } + private Element createRaceXMLWithoutRaceData(final Fleet fleet, final RaceColumn column, final Leaderboard leaderboard, int sameDayGroupIndex, int raceCounter, Util.Pair> raceConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { + Element raceElement = new Element("race"); + addNamedElementWithValue(raceElement, "name", column.getName()+'-'+fleet.getName()); + addNamedElementWithValue(raceElement, "race_index_in_leaderboard", raceCounter); + addNamedElementWithValue(raceElement, "fleet_name", fleet.getName()); + addNamedElementWithValue(raceElement, "same_day_index", sameDayGroupIndex); + addNamedElementWithValue(raceElement, "is_live", "false"); + TimePoint timepointToBeUsed = MillisecondsTimePoint.now(); + List competitors = leaderboard.getCompetitorsFromBestToWorst(column, timepointToBeUsed); + for (Competitor competitorInLeaderboard : competitors) { + MaxPointsReason mpr = leaderboard.getScoreCorrection().getMaxPointsReason(competitorInLeaderboard, column, timepointToBeUsed); + if (mpr != null && !mpr.equals(MaxPointsReason.NONE)) { + // add this competitor to the list to have him evaluated + Element competitorElement = createCompetitorXML(competitorInLeaderboard, leaderboard, /*shortVersion*/ true, null); + Element competitorRaceDataElement = new Element("competitor_race_data"); + MaxPointsReason maxPointsReason = leaderboard.getMaxPointsReason(competitorInLeaderboard, column, timepointToBeUsed); + addNamedElementWithValue(competitorRaceDataElement, "max_points_reason", maxPointsReason.toString()); + boolean isDiscardedForCompetitor = leaderboard.isDiscarded(competitorInLeaderboard, column, timepointToBeUsed); + addNamedElementWithValue(competitorRaceDataElement, "is_discarded", isDiscardedForCompetitor == true ? "true" : "false"); + Double finalRaceScore = leaderboard.getTotalPoints(competitorInLeaderboard, column, timepointToBeUsed); + addNamedElementWithValue(competitorRaceDataElement, "final_race_score", finalRaceScore); + competitorElement.addContent(competitorRaceDataElement); + raceElement.addContent(competitorElement); + raceConfidenceAndErrorMessages = updateConfidence("Competitor " + competitorInLeaderboard.getName() + " has no valid data for this race!", 0.2, raceConfidenceAndErrorMessages); + } + } + return raceElement; + } + /** * Creates elements containing information about a race */ @@ -233,7 +270,7 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(raceElement, "start_of_tracking_time_as_millis", handleValue(race.getStartOfTracking())); addNamedElementWithValue(raceElement, "end_of_tracking_time_as_millis", handleValue(race.getEndOfTracking())); - LineDetails start = race.getStartLine(race.getStartOfTracking()); + LineDetails start = race.getStartLine(race.getStartOfRace()); if (start != null) { if (start.getLength() != null) { addNamedElementWithValue(raceElement, "start_line_length_in_meters", start.getLength().getMeters()); @@ -568,13 +605,16 @@ public class LeaderboardData extends ExportAction { // will kick in } } - if (endOfRaceForLastTrackedRaceInLeaderboard != null) { - timePointOfLatestModification = endOfRaceForLastTrackedRaceInLeaderboard; + // first look for latest modification of leaderboard + // if there is no latest modification then take the last races time + if (leaderboard.getTimePointOfLatestModification() != null) { + timePointOfLatestModification = leaderboard.getTimePointOfLatestModification(); } else { - if (leaderboard.getTimePointOfLatestModification() != null) { - timePointOfLatestModification = leaderboard.getTimePointOfLatestModification(); + if (endOfRaceForLastTrackedRaceInLeaderboard != null) { + timePointOfLatestModification = endOfRaceForLastTrackedRaceInLeaderboard; } else { - timePointOfLatestModification = MillisecondsTimePoint.now(); + // make sure that the timepoint is far in the future - could be that the export is called too early + timePointOfLatestModification = MillisecondsTimePoint.now().plus(Duration.ONE_YEAR); } } Duration totalTimeSailed = leaderboard.getTotalTimeSailed(competitor, timePointOfLatestModification); @@ -708,6 +748,36 @@ public class LeaderboardData extends ExportAction { return legElement; } + private List createFullWindDataXML(TrackedRace race, Fleet fleet, int sameDayIndex) { + List windElements = new ArrayList<>(); + List windSourcesToDeliver = new ArrayList(); + WindSourceImpl windSource = new WindSourceImpl(WindSourceType.COMBINED); + windSourcesToDeliver.add(windSource); + TimePoint fromTimePoint = race.getStartOfRace(); + TimePoint toTimePoint = race.getEndOfRace(); + int numberOfFixes = (int) ((toTimePoint.asMillis() - fromTimePoint.asMillis()) / /*resolutionInMillis*/ 1000*60*5); + WindTrack windTrack = race.getOrCreateWindTrack(windSource); + TimePoint timePoint = fromTimePoint; + for (int i = 0; i < numberOfFixes && toTimePoint != null && timePoint.compareTo(toTimePoint) < 0; i++) { + WindWithConfidence> averagedWindWithConfidence = windTrack + .getAveragedWindWithConfidence(null, timePoint); + if (averagedWindWithConfidence != null) { + Element windElement = new Element("windFix"); + addNamedElementWithValue(windElement, "same_day_index", sameDayIndex); + addNamedElementWithValue(windElement, "race_name", race.getRace().getName()); + addNamedElementWithValue(windElement, "fleet_name", fleet.getName()); + windElement.addContent(createTimedXML("", averagedWindWithConfidence.getObject().getTimePoint())); + addNamedElementWithValue(windElement, "bearingInDegrees", averagedWindWithConfidence.getObject().getBearing().getDegrees()); + addNamedElementWithValue(windElement, "directionFromInDegrees", averagedWindWithConfidence.getObject().getBearing().reverse().getDegrees()); + windElement.addContent(createWindXML("", new SpeedWithConfidenceImpl(new KnotSpeedImpl(averagedWindWithConfidence.getObject().getKnots()), + averagedWindWithConfidence.getConfidence(), toTimePoint))); + windElements.add(windElement); + } + timePoint = new MillisecondsTimePoint(timePoint.asMillis() + 1000*60*5); + } + return windElements; + } + /** * Creates xml elements that indicate confidence. */ @@ -784,24 +854,29 @@ public class LeaderboardData extends ExportAction { private Util.Pair> checkData(TrackedRace race) throws Exception { double simpleConfidence = 1.0; Vector messages = new Vector(); TimePoint dateAtYear2000 = new MillisecondsTimePoint(946681200000l); - if (race.getStartOfRace() == null || race.getStartOfRace().before(dateAtYear2000)) { - messages.add("Start time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); + if (race == null) { + messages.add("Tracked race is null - no race seems to be connected!"); simpleConfidence -= 1; - } - if (race.getEndOfRace() == null || race.getEndOfRace().before(dateAtYear2000)) { - messages.add("End time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); - simpleConfidence -= 1; - } - if (race.getStartOfTracking() == null || race.getStartOfTracking().before(dateAtYear2000)) { - messages.add("Start tracking time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); - simpleConfidence -= 0.2; - } - if (race.getEndOfTracking() == null || race.getEndOfTracking().before(dateAtYear2000)) { - messages.add("End tracking time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); - simpleConfidence -= 0.2; - } - if (race.isLive(MillisecondsTimePoint.now())) { - messages.add("This race is live - data for this race will not be available until the race has been finished!"); + } else { + if (race.getStartOfRace() == null || race.getStartOfRace().before(dateAtYear2000)) { + messages.add("Start time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); + simpleConfidence -= 1; + } + if (race.getEndOfRace() == null || race.getEndOfRace().before(dateAtYear2000)) { + messages.add("End time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); + simpleConfidence -= 1; + } + if (race.getStartOfTracking() == null || race.getStartOfTracking().before(dateAtYear2000)) { + messages.add("Start tracking time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); + simpleConfidence -= 0.2; + } + if (race.getEndOfTracking() == null || race.getEndOfTracking().before(dateAtYear2000)) { + messages.add("End tracking time of race " + race.getRaceIdentifier().getRaceName() + " is either null or not valid!"); + simpleConfidence -= 0.2; + } + if (race.isLive(MillisecondsTimePoint.now())) { + messages.add("This race is live - data for this race will not be available until the race has been finished!"); + } } return new Util.Pair>(simpleConfidence, messages); } @@ -886,24 +961,25 @@ public class LeaderboardData extends ExportAction { TimePoint timeSpent = MillisecondsTimePoint.now(); log.info("Starting XML export of " + leaderboard.getName()); Util.Pair> leaderboardConfidenceAndErrorMessages = checkData(leaderboard); - final List racesElements = new ArrayList(); final List competitorElements = new ArrayList(); - for (Competitor competitor : leaderboard.getAllCompetitors()) { Util.Pair> competitorConfidenceAndErrorMessages = checkData(competitor); competitorElements.add(createCompetitorXML(competitor, leaderboard, /*shortVersion*/ false, competitorConfidenceAndErrorMessages)); } - + List windData = new ArrayList(); TrackedRace raceBefore = null; int sameDayGroupIndex = 0; int raceCounter = 0; for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { for (Fleet fleet : raceColumn.getFleets()) { TrackedRace trackedRace = raceColumn.getTrackedRace(fleet); - if (trackedRace != null && trackedRace.hasGPSData()) { + if (trackedRace != null) { sameDayGroupIndex += getSameDayGroupIndex(raceColumn.getTrackedRace(fleet), raceBefore); - TimePoint timeSpentForRace = MillisecondsTimePoint.now(); - Util.Pair> raceConfidenceAndErrorMessages = checkData(trackedRace); - final List legs = new ArrayList(); + } + TimePoint timeSpentForRace = MillisecondsTimePoint.now(); + Util.Pair> raceConfidenceAndErrorMessages = checkData(trackedRace); + final List legs = new ArrayList(); + if (trackedRace != null && trackedRace.hasGPSData()) { + windData.addAll(createFullWindDataXML(trackedRace, fleet, sameDayGroupIndex)); int legCounter = 0; for (TrackedLeg leg : trackedRace.getTrackedLegs()) { Util.Pair> legConfidenceAndErrorMessages = checkData(leg); @@ -914,12 +990,15 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(raceElement, "generation_time_in_milliseconds", elapsedTimeForRace.asMillis()); racesElements.add(raceElement); log.info("Exported complete race " + trackedRace.getRace().getName() + " in " + elapsedTimeForRace.asMillis() + " milliseconds!"); - raceBefore = trackedRace; + } else { + racesElements.add(createRaceXMLWithoutRaceData(fleet, raceColumn, leaderboard, sameDayGroupIndex, ++raceCounter, raceConfidenceAndErrorMessages)); + log.info("Exported empty race in race column " + raceColumn.getName() + " with fleet " + fleet.getName()); } + raceBefore = trackedRace; } } log.info("Finished XML export of leaderboard " + leaderboard.getName() + " in " + MillisecondsTimePoint.now().minus(timeSpent.asMillis()).asMillis() + " milliseconds"); - Element dataToExport = createLeaderboardXML(leaderboard, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages); + Element dataToExport = createLeaderboardXML(leaderboard, windData, competitorElements, racesElements, leaderboardConfidenceAndErrorMessages); sendDocument(dataToExport, leaderboard.getName() + ".xml"); } } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/resources/Highcharts.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/resources/Highcharts.java index 70e47a0a54e..8d765e9caf3 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/resources/Highcharts.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/resources/Highcharts.java @@ -13,9 +13,22 @@ public class Highcharts { public static LegacyResources LEGACY = GWT.create(LegacyResources.class); public static void ensureInjected() { ScriptInjector.fromString(LEGACY.highcharts().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject(); - ScriptInjector.fromString(LEGACY.highchartsMore().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject(); ScriptInjector.fromString(LEGACY.highchartsThemeGrid().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject(); + } + + public static void ensureInjectedWithMore() { + ensureInjected(); + ScriptInjector.fromString(LEGACY.highchartsMore().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject(); ScriptInjector.fromString(LEGACY.highchartsModuleExport().getText()).setWindow(ScriptInjector.TOP_WINDOW) .inject(); } + + public static void ensureInjectedWithExport() { + ensureInjected(); + ensureInjectedWithMore(); + ScriptInjector.fromString(LEGACY.highchartsMore().getText()).setWindow(ScriptInjector.TOP_WINDOW).inject(); + ScriptInjector.fromString(LEGACY.highchartsModuleExport().getText()).setWindow(ScriptInjector.TOP_WINDOW) + .inject(); + } + } diff --git a/java/com.sap.sse.security.ui/.tmp/gwt-work/.gitignore b/java/com.sap.sse.security.ui/.tmp/gwt-work/.gitignore new file mode 100644 index 00000000000..3e3e47daf0f --- /dev/null +++ b/java/com.sap.sse.security.ui/.tmp/gwt-work/.gitignore @@ -0,0 +1 @@ +com.* diff --git a/java/com.sap.sse.security.ui/Security UI sdm.launch b/java/com.sap.sse.security.ui/Security UI sdm.launch index fce3bab6e54..61f2e3c85a0 100755 --- a/java/com.sap.sse.security.ui/Security UI sdm.launch +++ b/java/com.sap.sse.security.ui/Security UI sdm.launch @@ -43,7 +43,7 @@ - +