Merge branch 'master' into bug3938

This commit is contained in:
Papick Garcia Taboada committed 2016-10-18 13:31:02 +02:00
commit e60b987a2d
16 files changed
+391 -255

No files matched your search

@@ -3,6 +3,7 @@ package com.sap.sailing.datamining.shared;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.impl.CentralAngleDistance;
import com.sap.sailing.domain.common.impl.MeterDistance;
@@ -20,6 +21,7 @@ public final class SailingDataMiningSerializationDummy implements SerializationD
private NauticalMileDistance nauticalMileDistance;
private ManeuverType maneuverType;
private Tack tack;
private NauticalSide nauticalSide;
private SailingDataMiningSerializationDummy() { }
@@ -48,10 +48,10 @@ Bft11=Violent storm
Bft12=Hurricane
RelativeScore=Relative Score (0 best; 1 worst)
AbsoluteRank=Absolute Rank
WindSpeedInBeaufort=Wind speed in Bft
DistanceAtStart=Distance to start at start
SpeedWhenStarting=Speed when starting
SpeedTenSecondsBeforeStart=Speed 10s before start
WindSpeedInBeaufort=Wind Speed in Bft
DistanceAtStart=Distance to Start at Start
SpeedWhenStarting=Speed at Start
SpeedTenSecondsBeforeStart=Speed 10s Before Start
RankAtFirstMark=Rank at First Mark
NumberOfManeuvers=Number of Maneuvers
NumberOfTacks=Number of Tacks
@@ -71,4 +71,9 @@ SpeedBefore=Speed Before Maneuver
SpeedAfter=Speed After Maneuver
DirectionChange=Direction Change in Degrees
ManeuverLoss=Maneuver Loss
Waypoint=Waypoint
Waypoint=Waypoint
PassingSide=Passing Side
TackAtStart=Tack at Start
SpeedTenSecondsAfterStart=Speed 10s After Start
RankThirtySecondsAfterStart=Rank 30s After Start
StartAdvantage=Start Advantage
@@ -66,9 +66,14 @@ IsTracked=Ist getracked
NumberOfCompetitorFixes=Anzahl der Teilnehmer-Fixes
NumberOfMarkFixes=Anzahl der Tonnen-Fixes
ManeuverType=Art des Manövers
Tack=Seite
Tack=Bug
SpeedBefore=Geschwindigkeit bevor dem Manöver
SpeedAfter=Geschwindigkeit nach dem Manöver
DirectionChange=Richtungsänderung in Grad
ManeuverLoss=Manöververlust
Waypoint=Wegpunkt
Waypoint=Wegpunkt
PassingSide=Passierende Seite
TackAtStart=Startbug
SpeedTenSecondsAfterStart=Geschwindigkeit 10s nach dem Startzeitpunkt
RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt
StartAdvantage=Startvorteil
@@ -1,6 +1,7 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
@@ -23,6 +24,9 @@ public interface HasMarkPassingContext {
@Connector(messageKey="Waypoint", ordinal=13)
Waypoint getWaypoint();
@Dimension(messageKey="PassingSide", ordinal=13)
NauticalSide getPassingSide();
@Connector(messageKey="SpeedBefore", ordinal=0)
SpeedWithBearing getSpeedBefore();
@@ -2,7 +2,9 @@ package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Dimension;
import com.sap.sse.datamining.annotations.Statistic;
@@ -18,34 +20,46 @@ public interface HasRaceOfCompetitorContext {
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
public String getCompetitorSearchTag();
@Dimension(messageKey="TackAtStart", ordinal=12)
public Tack getTackAtStart() throws NoWindException;
@Statistic(messageKey="DistanceAtStart", resultDecimals=2, ordinal=0)
public Distance getDistanceToStartLineAtStart();
@Statistic(messageKey="DistanceToStarboardSideAtStart", resultDecimals=2, ordinal=1)
public Double getNormalizedDistanceToStarboardSideAtStart();
// @Statistic(messageKey="StartAdvantage", resultDecimals=2, ordinal=2)
// public Double getNormalizedDistanceToFavoredEndAtStart();
@Connector(messageKey="SpeedWhenStarting", ordinal=2)
public Speed getSpeedWhenStarting();
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=3)
public Speed getSpeedTenSecondsBeforeStart();
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=4)
@Connector(messageKey="SpeedTenSecondsAfterStart", ordinal=4)
public Speed getSpeedTenSecondsAfterStart();
@Statistic(messageKey="RankThirtySecondsAfterStart", resultDecimals=2, ordinal=5)
public Double getAbsoluteRankThirtySecondsAfterStart();
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=6)
public Double getRankAtFirstMark();
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=5)
public int getNumberOfManeuvers();
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=6)
public int getNumberOfTacks();
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=7)
public int getNumberOfJibes();
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=8)
public int getNumberOfPenaltyCircles();
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=9)
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=7)
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish();
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=8)
public int getNumberOfManeuvers();
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=9)
public int getNumberOfTacks();
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=10)
public int getNumberOfJibes();
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=11)
public int getNumberOfPenaltyCircles();
}
@@ -4,6 +4,7 @@ import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.leaderboard.Leaderboard;
@@ -43,6 +44,11 @@ public class MarkPassingWithContext implements HasMarkPassingContext {
public Waypoint getWaypoint() {
return getManeuver().getWaypointPassed();
}
@Override
public NauticalSide getPassingSide() {
return getManeuver().getSide();
}
@Override
public SpeedWithBearing getSpeedBefore() {
@@ -9,7 +9,9 @@ import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -44,11 +46,31 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
return getCompetitor().getSearchTag();
}
@Override
public Tack getTackAtStart() throws NoWindException {
TimePoint startOfRace = getTrackedRace().getStartOfRace();
return startOfRace == null ? null : getTrackedRace().getTack(getCompetitor(), startOfRace);
}
@Override
public Distance getDistanceToStartLineAtStart() {
return getTrackedRace().getDistanceToStartLine(getCompetitor(), 0);
}
@Override
public Double getNormalizedDistanceToStarboardSideAtStart() {
TrackedRace trackedRace = getTrackedRace();
TrackedLegOfCompetitor firstTrackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, trackedRace.getRace().getCourse().getFirstLeg());
TimePoint competitorStartTime = firstTrackedLegOfCompetitor.getStartTime();
if (competitorStartTime == null) {
return null;
}
Double distance = trackedRace.getDistanceFromStarboardSideOfStartLine(getCompetitor(), competitorStartTime).getMeters();
Double length = trackedRace.getStartLine(competitorStartTime).getLength().getMeters();
return distance / length;
}
@Override
public Speed getSpeedWhenStarting() {
return getTrackedRace().getSpeedWhenCrossingStartLine(getCompetitor());
@@ -59,6 +81,26 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
return getTrackedRace().getSpeed(getCompetitor(), TimeUnit.SECONDS.toMillis(10));
}
@Override
public Speed getSpeedTenSecondsAfterStart() {
TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace == null) {
return null;
}
return getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(startOfRace.plus(TimeUnit.SECONDS.toMillis(10)));
}
@Override
public Double getAbsoluteRankThirtySecondsAfterStart() {
TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace == null) {
return null;
}
int rank = getTrackedRace().getRank(getCompetitor(), startOfRace.plus(TimeUnit.SECONDS.toMillis(30)));
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public Double getRankAtFirstMark() {
Course course = getTrackedRace().getRace().getCourse();
@@ -68,6 +110,18 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
Double rankAtFirstMark = getRankAtFirstMark();
Double rankAtFinish = getRankAtFinish();
return rankAtFirstMark != null && rankAtFinish != null ? rankAtFirstMark - rankAtFinish : null;
}
private Double getRankAtFinish() {
int rank = getTrackedRace().getRank(getCompetitor(), getTrackedRace().getEndOfTracking());
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public int getNumberOfManeuvers() {
return getNumberOfTacks() + getNumberOfJibes();
@@ -98,27 +152,5 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
}
return number;
}
@Override
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
Double rankAtFirstMark = getRankAtFirstMark();
Double rankAtFinish = getRankAtFinish();
return rankAtFirstMark != null && rankAtFinish != null ? rankAtFirstMark - rankAtFinish : null;
}
private Double getRankAtFinish() {
int rank = getTrackedRace().getRank(getCompetitor(), getTrackedRace().getEndOfTracking());
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public Double getNormalizedDistanceToStarboardSideAtStart() {
TrackedRace trackedRace = getTrackedRace();
TrackedLegOfCompetitor firstTrackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, trackedRace.getRace().getCourse().getFirstLeg());
TimePoint competitorStartTime = firstTrackedLegOfCompetitor.getStartTime();
Double distance = trackedRace.getDistanceFromStarboardSideOfStartLine(getCompetitor(), competitorStartTime).getMeters();
Double length = trackedRace.getStartLine(competitorStartTime).getLength().getMeters();
return distance / length;
}
}
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill=
"#FFF" d="M10,2a8,8,0,1,1-8,8,8,8,0,0,1,8-8m0-2A10,10,0,1,0,20,10,10,10,0,0,0,
10,0h0ZM9.71,10l0.08,0L9.71,10V10m-2-3.43v7l3.07-1.74L13.86,10,10.78,8.26,7.71,
6.52h0Z"/></svg>

After

Width:  |  Height:  |  Size: 252 B

@@ -1,14 +1,14 @@
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared"
xmlns:dp="urn:import:com.sap.sailing.gwt.home.desktop.partials">
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.racelist.RaceListResources" />
<g:HTMLPanel addStyleNames="{local_res.css.raceslistlive}">
<h3 ui:field="titleUi"></h3>
<div style="display: none;" ui:field="infoUi"></div>
<div style="display: none; font-weight: bold; font-style: italic;" ui:field="noContentPlaceholderUi"></div>
<dp:racelist.AbstractRaceList ui:field="raceListUi"/>
</g:HTMLPanel>
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared"
xmlns:dp="urn:import:com.sap.sailing.gwt.home.desktop.partials">
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.racelist.RaceListResources" />
<g:HTMLPanel addStyleNames="{local_res.css.raceslistlive}">
<h3 ui:field="titleUi"></h3>
<div style="display: none;" ui:field="infoUi"></div>
<div style="display: none; font-weight: bold; font-style: italic;" ui:field="noContentPlaceholderUi"></div>
<dp:racelist.AbstractRaceList ui:field="raceListUi"/>
</g:HTMLPanel>
</ui:UiBinder>
@@ -63,6 +63,7 @@
.raceviewerlaunchpad:hover {
cursor: pointer;
background: #0081b8;
text-decoration: none;
}
.raceviewerlaunchpadlive {
background: #e94a1b;
@@ -79,11 +80,29 @@
.raceviewerlaunchpadlive .raceviewerlaunchpad_content_item_icon svg {
fill: #e94a1b;
}
.raceviewerlaunchpadplanned {
background: #999999;
}
.raceviewerlaunchpadplanned:hover {
background: #8a8a8a;
}
.raceviewerlaunchpadplanned .raceviewerlaunchpad_content_item {
color: #999999;
}
.raceviewerlaunchpadplanned .raceviewerlaunchpad_content_item:hover {
background: #999999;
}
.raceviewerlaunchpadplanned .raceviewerlaunchpad_content_item_icon svg {
fill: #999999;
}
.raceviewerlaunchpad_icon {
margin-left: 0.666666666666667em;
width: 1.666666666666667em;
height: 1.333333333333333em;
}
.raceviewerlaunchpad_icon img {
height: 100%;
}
.raceviewerlaunchpad_icon svg {
width: 100%;
height: 100%;
@@ -1,85 +1,110 @@
package com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
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.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.desktop.places.event.EventView;
import com.sap.sailing.gwt.home.desktop.places.event.EventView.Presenter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.common.Util;
public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends AbstractCell<T> {
interface CellTemplates extends SafeHtmlTemplates {
@Template("<div class=\"{0}\">{1}</div>")
SafeHtml raceNotTracked(String styleNames, String text);
@Template("<div class=\"{0}\"><div>{2}</div><div class=\"{1}\"><img src=\"images/home/launch-loupe.svg\"/></div>")
SafeHtml raceviewerLaunchPad(String styleNames, String iconStyleNames, String text);
}
private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class);
private static final StringMessages I18N = StringMessages.INSTANCE;
private final RaceviewerLaunchPadResources local_res = RaceviewerLaunchPadResources.INSTANCE;
private final String notTrackedStyleNames = local_res.css().raceviewerlaunchpad_not_tracked();
private final String analyzeStyleNames = local_res.css().raceviewerlaunchpad();
private final String liveStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadlive());
private final String iconStyleNames = local_res.css().raceviewerlaunchpad_icon();
private final EventView.Presenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
public RaceviewerLaunchPadCell(Presenter presenter) {
super(BrowserEvents.CLICK);
local_res.css().ensureInjected();
this.presenter = presenter;
}
@Override
public void onBrowserEvent(Context context, final Element parent, T data, NativeEvent event,
ValueUpdater<T> valueUpdater) {
if (data.hasValidTrackingData() && BrowserEvents.CLICK.equals(event.getType())
&& parent.getFirstChildElement().isOrHasChild(Element.as(event.getEventTarget()))) {
panel.setWidget(new RaceviewerLaunchPad(data, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
return presenter.getRaceViewerURL(data, mode);
}
});
panel.setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
int alignBottom = parent.getAbsoluteTop() + parent.getOffsetHeight() - offsetHeight;
int top = (alignBottom - Window.getScrollTop() < 0 ? parent.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(parent.getAbsoluteRight() + 1 - offsetWidth, top);
panel.getElement().scrollIntoView();
}
});
return;
}
super.onBrowserEvent(context, parent, data, event, valueUpdater);
}
@Override
public void render(Context context, T data, SafeHtmlBuilder sb) {
if (data.hasValidTrackingData()) {
String styleNames = data.isFinished() ? analyzeStyleNames : liveStyleNames;
sb.append(TEMPLATE.raceviewerLaunchPad(styleNames, iconStyleNames, I18N.raceDetailsToShow()));
} else {
package com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
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.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.desktop.places.event.EventView;
import com.sap.sailing.gwt.home.desktop.places.event.EventView.Presenter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
import com.sap.sse.common.Util;
public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends AbstractCell<T> {
interface CellTemplates extends SafeHtmlTemplates {
@Template("<div class=\"{0}\">{1}</div>")
SafeHtml raceNotTracked(String styleNames, String text);
@Template("<div class=\"{0}\"><div>{2}</div><div class=\"{1}\"><img src=\"images/home/launch-loupe.svg\"/></div>")
SafeHtml raceviewerLaunchPad(String styleNames, String iconStyleNames, String text);
@Template("<a href=\"{4}\" target=\"_blank\" class=\"{0}\"><div>{2}</div><div class=\"{1}\"><img src=\"images/home/{3}.svg\"/></div></a> ")
SafeHtml standaloneButton(String styleNames, String iconStyleNames, String text, String icon, String link);
}
private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class);
private static final StringMessages I18N = StringMessages.INSTANCE;
private final RaceviewerLaunchPadResources local_res = RaceviewerLaunchPadResources.INSTANCE;
private final String notTrackedStyleNames = local_res.css().raceviewerlaunchpad_not_tracked();
private final String analyzeStyleNames = local_res.css().raceviewerlaunchpad();
private final String plannedStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadplanned());
private final String liveStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadlive());
private final String iconStyleNames = local_res.css().raceviewerlaunchpad_icon();
private final EventView.Presenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
public RaceviewerLaunchPadCell(Presenter presenter) {
super(BrowserEvents.CLICK);
local_res.css().ensureInjected();
this.presenter = presenter;
}
@Override
public void onBrowserEvent(Context context, final Element parent, T data, NativeEvent event,
ValueUpdater<T> valueUpdater) {
if (!renderAsDirectLinkButton(data)) {
if (data.hasValidTrackingData() && BrowserEvents.CLICK.equals(event.getType())
&& parent.getFirstChildElement().isOrHasChild(Element.as(event.getEventTarget()))) {
panel.setWidget(createLaunchPad(parent.getFirstChildElement(), data));
panel.setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
int alignBottom = parent.getAbsoluteTop() + parent.getOffsetHeight() - offsetHeight;
int top = (alignBottom - Window.getScrollTop() < 0 ? parent.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(parent.getAbsoluteRight() + 1 - offsetWidth, top);
panel.getElement().scrollIntoView();
}
});
}
return;
}
super.onBrowserEvent(context, parent, data, event, valueUpdater);
}
@Override
public void render(Context context, T data, SafeHtmlBuilder sb) {
if (data.hasValidTrackingData()) {
if (renderAsDirectLinkButton(data)) {
sb.append(TEMPLATE.standaloneButton(plannedStyleNames, iconStyleNames, I18N.watchLive(),
"launch-play", presenter.getRaceViewerURL(data, RaceBoardModes.PLAYER.name())));
} else {
String styleNames = data.isFinished() ? analyzeStyleNames : liveStyleNames;
sb.append(TEMPLATE.raceviewerLaunchPad(styleNames, iconStyleNames, I18N.raceDetailsToShow()));
}
} else {
sb.append(TEMPLATE.raceNotTracked(notTrackedStyleNames, I18N.eventRegattaRaceNotTracked()));
}
}
}
}
}
/**
* Creates a {@link RaceviewerLaunchPad} for the given data object.
*/
private RaceviewerLaunchPad createLaunchPad(Element button, T data) {
RaceviewerLaunchPad launchPad = new RaceviewerLaunchPad(data, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String raceBoardMode) {
return presenter.getRaceViewerURL(data, raceBoardMode);
}
};
return launchPad;
}
/** Determine whether a direct link button should be/is rendered instead of a menu popup. */
private boolean renderAsDirectLinkButton(T data) {
return !data.isFinished() && !data.isRunning();
}
}
@@ -18,6 +18,7 @@ public interface RaceviewerLaunchPadResources extends ClientBundle {
String raceviewerlaunchpad_date();
String raceviewerlaunchpad_cta();
String raceviewerlaunchpadlive();
String raceviewerlaunchpadplanned();
String raceviewerlaunchpad_content_item();
String raceviewerlaunchpad_content_item_icon();
String raceviewerlaunchpad_icon();
@@ -174,6 +174,9 @@
.fleet_races_raceplanned:hover {
background-color: #8a8a8a;
}
.fleet_races_raceplanned .fleet_races_race_name:after {
background: url("images/home/launch-play.svg") no-repeat center center;
}
.fleet_races_raceuntracked .fleet_races_race_name:after {
display: none;
}
@@ -69,6 +69,7 @@
.regattacompetition_phase_fleetfullwidth .regattacompetition_phase_fleet_race_arrow {
display: block;
width: 1.6666666667em;
height: 1.4444444444em;
}
.regattacompetition_phase_fleet_title {
font-size: 1.066666666666667em;
@@ -117,9 +118,6 @@
background-color: #999;
font-style: italic;
}
.regattacompetition_phase_fleet_raceplanned .regattacompetition_phase_fleet_race_title:after {
display: none;
}
.regattacompetition_phase_fleet_racelive.regattacompetition_phase_fleet_raceuntracked {
color: #e94a1b;
background-color: #fff;
@@ -150,6 +148,7 @@
}
.regattacompetition_phase_fleet_race_arrow {
display: none;
background: url("images/home/launch-loupe.svg") no-repeat center center;
position: absolute;
top: 50%;
right: 1em;
@@ -158,6 +157,12 @@
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
.regattacompetition_phase_fleet_raceplanned .regattacompetition_phase_fleet_race_title:after {
background: url("images/home/launch-play.svg") no-repeat center center;
}
.regattacompetition_phase_fleet_raceplanned .regattacompetition_phase_fleet_race_arrow {
background: url("images/home/launch-play.svg") no-repeat center center;
}
.regattacompetition_phase_fleet_raceuntracked .regattacompetition_phase_fleet_race_arrow {
display: none;
}
@@ -10,6 +10,6 @@
<div class="{local_res.css.regattacompetition_phase_fleet_race_state}" ui:field="raceStateUi"></div>
<div class="{local_res.css.regattacompetition_phase_fleet_race_date}" ui:field="raceDateUi"></div>
</div>
<img src="images/home/launch-loupe.svg" class="{local_res.css.regattacompetition_phase_fleet_race_arrow}"></img>
<div class="{local_res.css.regattacompetition_phase_fleet_race_arrow}"></div>
</div>
</ui:UiBinder>
@@ -1,111 +1,122 @@
package com.sap.sailing.gwt.home.shared.partials.regattacompetition;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.shortTimeFormatter;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.weekdayMonthAbbrDayDateFormatter;
import static com.sap.sse.common.impl.MillisecondsTimePoint.now;
import java.util.Date;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.text.client.DateTimeFormatRenderer;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.common.client.DateUtil;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceTrackingState;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceViewState;
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPad;
import com.sap.sailing.gwt.home.shared.partials.regattacompetition.RegattaCompetitionView.RegattaCompetitionRaceView;
import com.sap.sailing.gwt.ui.client.StringMessages;
public abstract class AbstractRegattaCompetitionFleetRace extends Widget implements RegattaCompetitionRaceView {
private static final StringMessages I18N = StringMessages.INSTANCE;
protected final Element mainElement;
private final SimpleRaceMetadataDTO race;
private final RegattaCompetitionPresenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
protected AbstractRegattaCompetitionFleetRace(final SimpleRaceMetadataDTO race,
RegattaCompetitionPresenter presenter) {
this.race = race;
this.presenter = presenter;
this.mainElement = getMainUiElement();
setupRaceState(race.getTrackingState(), race.getViewState());
getRaceNameUiElement().setInnerText(race.getRaceName());
setupRaceStart(race.getStart());
setElement(mainElement);
if (race.hasValidTrackingData()) {
sinkEvents(Event.ONCLICK);
}
}
@Override
public void onBrowserEvent(Event event) {
if (race.hasValidTrackingData() && event.getTypeInt() == Event.ONCLICK) {
this.getElement().scrollIntoView();
panel.setWidget(new RaceviewerLaunchPad(race, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
return presenter.getRaceViewerURL(data, mode);
}
});
panel.setVisible(false);
panel.show();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
Widget button = AbstractRegattaCompetitionFleetRace.this, panelContent = panel.getWidget();
int alignRight = button.getAbsoluteLeft() + button.getOffsetWidth() - panelContent.getOffsetWidth();
int left = (alignRight - Window.getScrollLeft() < 0 ? button.getAbsoluteLeft() - 1 : alignRight + 1);
int alignBottom = button.getAbsoluteTop() + button.getOffsetHeight() - panelContent.getOffsetHeight();
int top = (alignBottom - Window.getScrollTop() < 0 ? button.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(left, top);
panel.setVisible(true);
}
});
}
}
private void setupRaceState(RaceTrackingState trackingState, RaceViewState viewState) {
boolean isUntrackedRace = trackingState != RaceTrackingState.TRACKED_VALID_DATA;
if (viewState == RaceViewState.RUNNING) {
mainElement.addClassName(getRaceLiveStyleName());
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.live() : I18N.actionWatch());
} else if (viewState == RaceViewState.FINISHED) {
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.raceIsFinished() : I18N.actionAnalyze());
} else {
mainElement.addClassName(getRacePlannedStyleName());
if (viewState == RaceViewState.SCHEDULED) getRaceStateUiElement().setInnerText(I18N.raceIsPlanned());
else getRaceStateUiElement().setInnerText(viewState.getLabel());
}
setStyleName(mainElement, getRaceUntrackedStyleName(), isUntrackedRace);
}
private void setupRaceStart(Date startDate) {
if (startDate != null) {
boolean showTime = DateUtil.isSameDayOfMonth(now().asDate(), startDate);
DateTimeFormatRenderer renderer = showTime ? shortTimeFormatter : weekdayMonthAbbrDayDateFormatter;
getRaceDateUiElement().setInnerText(renderer.render(startDate));
}
}
protected abstract Element getMainUiElement();
protected abstract Element getRaceNameUiElement();
protected abstract Element getRaceStateUiElement();
protected abstract Element getRaceDateUiElement();
protected abstract String getRaceLiveStyleName();
protected abstract String getRacePlannedStyleName();
protected abstract String getRaceUntrackedStyleName();
}
package com.sap.sailing.gwt.home.shared.partials.regattacompetition;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.shortTimeFormatter;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.weekdayMonthAbbrDayDateFormatter;
import static com.sap.sse.common.impl.MillisecondsTimePoint.now;
import java.util.Date;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.text.client.DateTimeFormatRenderer;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.common.client.DateUtil;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceTrackingState;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceViewState;
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPad;
import com.sap.sailing.gwt.home.shared.partials.regattacompetition.RegattaCompetitionView.RegattaCompetitionRaceView;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
public abstract class AbstractRegattaCompetitionFleetRace extends Widget implements RegattaCompetitionRaceView {
private static final StringMessages I18N = StringMessages.INSTANCE;
protected final Element mainElement;
private final SimpleRaceMetadataDTO race;
private final RegattaCompetitionPresenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
protected AbstractRegattaCompetitionFleetRace(final SimpleRaceMetadataDTO race,
RegattaCompetitionPresenter presenter) {
this.race = race;
this.presenter = presenter;
this.mainElement = getMainUiElement();
setupRaceState(race.getTrackingState(), race.getViewState());
getRaceNameUiElement().setInnerText(race.getRaceName());
setupRaceStart(race.getStart());
setElement(mainElement);
if (race.hasValidTrackingData()) {
sinkEvents(Event.ONCLICK);
}
}
@Override
public void onBrowserEvent(Event event) {
// Only handle click events if valid tracking data is available
if (!race.hasValidTrackingData() || event.getTypeInt() != Event.ONCLICK) {
return;
}
// If rendered as direct link button, open link in new tab directly instead of showing the menu popup
if (!race.isFinished() && !race.isRunning()) {
Window.open(presenter.getRaceViewerURL(race, RaceBoardModes.PLAYER.name()), "_blank", "");
return;
}
// Show menu popup relative to button by ensuring it stays with the viewport
this.getElement().scrollIntoView();
panel.setWidget(new RaceviewerLaunchPad(race, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
return presenter.getRaceViewerURL(data, mode);
}
});
panel.setVisible(false);
panel.show();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
Widget button = AbstractRegattaCompetitionFleetRace.this, panelContent = panel.getWidget();
int alignRight = button.getAbsoluteLeft() + button.getOffsetWidth() - panelContent.getOffsetWidth();
int left = (alignRight - Window.getScrollLeft() < 0 ? button.getAbsoluteLeft() - 1 : alignRight + 1);
int alignBottom = button.getAbsoluteTop() + button.getOffsetHeight() - panelContent.getOffsetHeight();
int top = (alignBottom - Window.getScrollTop() < 0 ? button.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(left, top);
panel.setVisible(true);
}
});
}
private void setupRaceState(RaceTrackingState trackingState, RaceViewState viewState) {
boolean isUntrackedRace = trackingState != RaceTrackingState.TRACKED_VALID_DATA;
if (viewState == RaceViewState.RUNNING) {
mainElement.addClassName(getRaceLiveStyleName());
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.live() : I18N.actionWatch());
} else if (viewState == RaceViewState.FINISHED) {
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.raceIsFinished() : I18N.actionAnalyze());
} else {
mainElement.addClassName(getRacePlannedStyleName());
if (viewState == RaceViewState.SCHEDULED) getRaceStateUiElement().setInnerText(I18N.raceIsPlanned());
else getRaceStateUiElement().setInnerText(viewState.getLabel());
}
setStyleName(mainElement, getRaceUntrackedStyleName(), isUntrackedRace);
}
private void setupRaceStart(Date startDate) {
if (startDate != null) {
boolean showTime = DateUtil.isSameDayOfMonth(now().asDate(), startDate);
DateTimeFormatRenderer renderer = showTime ? shortTimeFormatter : weekdayMonthAbbrDayDateFormatter;
getRaceDateUiElement().setInnerText(renderer.render(startDate));
}
}
protected abstract Element getMainUiElement();
protected abstract Element getRaceNameUiElement();
protected abstract Element getRaceStateUiElement();
protected abstract Element getRaceDateUiElement();
protected abstract String getRaceLiveStyleName();
protected abstract String getRacePlannedStyleName();
protected abstract String getRaceUntrackedStyleName();
}