Merge remote branch 'origin/505worlds2014' into 505worlds2014

This commit is contained in:
EC2 Server committed 2014-08-15 08:09:41 +00:00
commit 98ca707e7d
14 files changed
+333 -99

No files matched your search

@@ -13,7 +13,7 @@ public class TrackedRaceStatisticsDTO implements Serializable {
public boolean hasVideoData;
public boolean hasMeasuredWindData;
public boolean hasLegProgressData;
public boolean hasLeaderData;
public boolean hasLeaderOrWinnerData;
public Duration averageGPSDataSampleInterval;
public Integer measuredWindSourcesCount;
@@ -33,6 +33,6 @@ public class TrackedRaceStatisticsDTO implements Serializable {
hasVideoData= false;
hasMeasuredWindData = false;
hasLegProgressData = false;
hasLeaderData = false;
hasLeaderOrWinnerData = false;
}
}
@@ -17,6 +17,7 @@ import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.domain.common.dto.TrackedRaceDTO;
import com.sap.sailing.domain.common.dto.TrackedRaceStatisticsDTO;
import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -56,7 +57,8 @@ public interface DomainFactory extends SharedDomainFactory {
TrackedRaceDTO createTrackedRaceDTO(TrackedRace trackedRace);
TrackedRaceStatisticsDTO createTrackedRaceStatisticsDTO(TrackedRace trackedRace, Collection<MediaTrack> mediatracks);
TrackedRaceStatisticsDTO createTrackedRaceStatisticsDTO(TrackedRace trackedRace, Leaderboard leaderboard, RaceColumn raceColumn,
Fleet fleet, Collection<MediaTrack> mediatracks);
/**
* @param trackedRace must not be <code>null</code>
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.ObjectInputStreamResolvingAgainstDomainFactory;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
@@ -35,6 +36,7 @@ import com.sap.sailing.domain.common.dto.TrackedRaceDTO;
import com.sap.sailing.domain.common.dto.TrackedRaceStatisticsDTO;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.media.MediaTrack;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.leaderboard.impl.HighPointExtremeSailingSeriesOverall;
@@ -158,17 +160,31 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain
}
@Override
public TrackedRaceStatisticsDTO createTrackedRaceStatisticsDTO(TrackedRace trackedRace, Collection<MediaTrack> mediaTracks) {
public TrackedRaceStatisticsDTO createTrackedRaceStatisticsDTO(TrackedRace trackedRace, Leaderboard leaderboard,
RaceColumn raceColumn, Fleet fleet, Collection<MediaTrack> mediaTracks) {
TrackedRaceStatisticsDTO statisticsDTO = new TrackedRaceStatisticsDTO();
// GPS data
statisticsDTO.hasGPSData = trackedRace.hasGPSData();
Competitor leaderOrWinner = null;
TimePoint now = MillisecondsTimePoint.now();
try {
Competitor overallLeader = trackedRace.getOverallLeader(MillisecondsTimePoint.now());
if(overallLeader != null) {
statisticsDTO.hasLeaderData = true;
statisticsDTO.leaderOrWinner = convertToCompetitorDTO(overallLeader);
GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(overallLeader);
if(trackedRace.isLive(now)) {
leaderOrWinner = trackedRace.getOverallLeader(now);
} else if (trackedRace.getEndOfRace() != null) {
for(Competitor competitor: leaderboard.getCompetitorsFromBestToWorst(raceColumn, now)) {
Fleet fleetOfCompetitor = raceColumn.getFleetOfCompetitor(competitor);
if(fleetOfCompetitor != null && fleetOfCompetitor.equals(fleet)) {
leaderOrWinner = competitor;
break;
}
}
}
if(leaderOrWinner != null) {
statisticsDTO.hasLeaderOrWinnerData = true;
statisticsDTO.leaderOrWinner = convertToCompetitorDTO(leaderOrWinner);
GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(leaderOrWinner);
if(track != null) {
statisticsDTO.averageGPSDataSampleInterval = track.getAverageIntervalBetweenFixes();
}
@@ -563,6 +563,12 @@ img.openColumn {
background-position: 4% 50%, 96% 50%
}
.gwt-SplitLayoutPanel-NorthSouthToggleButton-Small-media {
background-position: 50% 50%, 100% 50% !important;
margin-left: 0px !important;
padding: 2px 20px !important;
}
.RegattaRaceInformation-Header {
position: relative;
left: 140px;
@@ -7,6 +7,7 @@ import com.google.gwt.user.client.Window;
import com.sap.sailing.gwt.home.client.place.aboutus.AboutUsPlace;
import com.sap.sailing.gwt.home.client.place.contact.ContactPlace;
import com.sap.sailing.gwt.home.client.place.event.EventPlace;
import com.sap.sailing.gwt.home.client.place.event.EventPlace.NavigationTabs;
import com.sap.sailing.gwt.home.client.place.events.EventsPlace;
import com.sap.sailing.gwt.home.client.place.searchresult.SearchResultPlace;
import com.sap.sailing.gwt.home.client.place.solutions.SolutionsPlace;
@@ -15,7 +16,7 @@ import com.sap.sailing.gwt.home.client.place.start.StartPlace;
public class PlaceNavigatorImpl implements PlaceNavigator {
private final PlaceController placeController;
private final static String DEFAULT_SAPSAILING_SERVER = "www.sapsailing.com"; // newhome.sapsailing.com
private final static String DEFAULT_SAPSAILING_SERVER = "newhome.sapsailing.com"; // www.sapsailing.com
private final static String DEFAULT_SAPSAILING_SERVER_URL = "http://" + DEFAULT_SAPSAILING_SERVER;
protected PlaceNavigatorImpl(PlaceController placeController) {
@@ -30,13 +31,13 @@ public class PlaceNavigatorImpl implements PlaceNavigator {
@Override
public void goToEvent(String eventUuidAsString, String baseUrl, boolean isOnRemoteServer) {
EventPlace eventPlace = new EventPlace(eventUuidAsString, null);
EventPlace eventPlace = new EventPlace(eventUuidAsString, NavigationTabs.Regattas, null);
gotoPlace(baseUrl, isOnRemoteServer, eventPlace, new EventPlace.Tokenizer());
}
@Override
public void goToRegattaOfEvent(String eventUuidAsString, String leaderboardIdAsNameString, String baseUrl, boolean isOnRemoteServer) {
EventPlace eventPlace = new EventPlace(eventUuidAsString, leaderboardIdAsNameString);
EventPlace eventPlace = new EventPlace(eventUuidAsString, NavigationTabs.Regatta, leaderboardIdAsNameString);
gotoPlace(baseUrl, isOnRemoteServer, eventPlace, new EventPlace.Tokenizer());
}
@@ -6,16 +6,30 @@ import com.sap.sailing.gwt.home.client.AbstractBasePlace;
public class EventPlace extends AbstractBasePlace {
private final String eventUuidAsString;
private final String leaderboardIdAsNameString;
private final NavigationTabs navigationTab;
private final static String PARAM_EVENTID = "eventId";
private final static String PARAM_LEADEROARD_NAME = "leaderboardName";
private final static String PARAM_NAVIGATION_TAB = "navigationTab";
public enum NavigationTabs { Overview, Regattas, Regatta, Media, Schedule };
public EventPlace(String url) {
super(url);
eventUuidAsString = getParameter("eventId");
leaderboardIdAsNameString = getParameter("leaderboardName");
eventUuidAsString = getParameter(PARAM_EVENTID);
leaderboardIdAsNameString = getParameter(PARAM_LEADEROARD_NAME);
String paramNavTab = getParameter(PARAM_NAVIGATION_TAB);
if(paramNavTab != null) {
navigationTab = NavigationTabs.valueOf(paramNavTab);
} else {
navigationTab = NavigationTabs.Regattas;
}
}
public EventPlace(String eventUuidAsString, String leaderboardIdAsNameString) {
super("eventId", eventUuidAsString, "leaderboardName", leaderboardIdAsNameString);
public EventPlace(String eventUuidAsString, NavigationTabs navigationTab, String leaderboardIdAsNameString) {
super(PARAM_EVENTID, eventUuidAsString, PARAM_NAVIGATION_TAB, navigationTab.name(), PARAM_LEADEROARD_NAME, leaderboardIdAsNameString);
this.eventUuidAsString = eventUuidAsString;
this.navigationTab = navigationTab;
this.leaderboardIdAsNameString = leaderboardIdAsNameString;
}
@@ -27,6 +41,10 @@ public class EventPlace extends AbstractBasePlace {
return leaderboardIdAsNameString;
}
public NavigationTabs getNavigationTab() {
return navigationTab;
}
public static class Tokenizer implements PlaceTokenizer<EventPlace> {
@Override
public String getToken(EventPlace place) {
@@ -6,14 +6,18 @@ import java.util.List;
import java.util.Map;
import com.google.gwt.core.client.GWT;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceTokenizer;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.History;
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.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.gwt.home.client.place.event.EventPlace.NavigationTabs;
import com.sap.sailing.gwt.home.client.place.event.header.EventHeader;
import com.sap.sailing.gwt.home.client.place.event.media.EventMedia;
import com.sap.sailing.gwt.home.client.place.event.overview.EventOverview;
@@ -99,12 +103,18 @@ public class TabletAndDesktopEventView extends Composite implements EventView, E
public void goToOverview() {
setVisibleEventElement(eventOverview);
eventHeader.setDataNavigationType("normal");
EventPlace place = new EventPlace(event.id.toString(), NavigationTabs.Overview, null);
pushPlaceToHistoryStack(place, new EventPlace.Tokenizer());
}
@Override
public void goToRegattas() {
setVisibleEventElement(eventRegattaList);
eventHeader.setDataNavigationType("normal");
EventPlace place = new EventPlace(event.id.toString(), NavigationTabs.Regattas, null);
pushPlaceToHistoryStack(place, new EventPlace.Tokenizer());
}
@Override
@@ -112,18 +122,27 @@ public class TabletAndDesktopEventView extends Composite implements EventView, E
eventRegattaRaces.setRaces(leaderboardGroup, leaderboard, raceGroup);
eventHeader.setDataNavigationType("compact");
setVisibleEventElement(eventRegattaRaces);
EventPlace place = new EventPlace(event.id.toString(), NavigationTabs.Regatta, leaderboard.name);
pushPlaceToHistoryStack(place, new EventPlace.Tokenizer());
}
@Override
public void goToSchedule() {
setVisibleEventElement(eventSchedule);
eventHeader.setDataNavigationType("normal");
EventPlace place = new EventPlace(event.id.toString(), NavigationTabs.Schedule, null);
pushPlaceToHistoryStack(place, new EventPlace.Tokenizer());
}
@Override
public void goToMedia() {
setVisibleEventElement(eventMedia);
eventHeader.setDataNavigationType("normal");
EventPlace place = new EventPlace(event.id.toString(), NavigationTabs.Media, null);
pushPlaceToHistoryStack(place, new EventPlace.Tokenizer());
}
@Override
@@ -205,4 +224,10 @@ public class TabletAndDesktopEventView extends Composite implements EventView, E
}
return result;
}
private <T extends Place> void pushPlaceToHistoryStack(T destinationPlace, PlaceTokenizer<T> tokenizer) {
String placeHistoryToken = destinationPlace.getClass().getSimpleName() + ":" + tokenizer.getToken(destinationPlace);
History.newItem(placeHistoryToken, false);
}
}
@@ -25,7 +25,7 @@ import com.sap.sse.gwt.client.player.Timer;
public class EventRegattaRacesRace extends UIObject {
private static EventRegattaRacesRaceUiBinder uiBinder = GWT.create(EventRegattaRacesRaceUiBinder.class);
private enum SimpleRaceStates { NOT_TRACKED, TRACKED_AND_FINISHED, TRACKED_AND_RUNNING, TRACKED_BUT_NOT_SCHEDULED };
private enum SimpleRaceStates { NOT_TRACKED, TRACKED, TRACKED_AND_LIVE, TRACKED_BUT_NOT_SCHEDULED };
interface EventRegattaRacesRaceUiBinder extends UiBinder<DivElement, EventRegattaRacesRace> {
}
@@ -128,18 +128,11 @@ public class EventRegattaRacesRace extends UIObject {
SimpleRaceStates simpleRaceState = SimpleRaceStates.NOT_TRACKED;
if(race != null && race.trackedRace != null) {
simpleRaceState = SimpleRaceStates.TRACKED;
if(isLive()) {
simpleRaceState = SimpleRaceStates.TRACKED_AND_RUNNING;
simpleRaceState = SimpleRaceStates.TRACKED_AND_LIVE;
if(race.startOfRace == null) {
simpleRaceState = SimpleRaceStates.TRACKED_BUT_NOT_SCHEDULED;
} else if (race.endOfRace != null) {
simpleRaceState = SimpleRaceStates.TRACKED_AND_FINISHED;
}
} else {
if(race.startOfRace == null) {
simpleRaceState = SimpleRaceStates.TRACKED_BUT_NOT_SCHEDULED;
} else if (race.endOfRace != null) {
simpleRaceState = SimpleRaceStates.TRACKED_AND_FINISHED;
}
}
}
@@ -164,7 +157,7 @@ public class EventRegattaRacesRace extends UIObject {
case TRACKED_BUT_NOT_SCHEDULED:
showElement(raceNotScheduledDiv);
break;
case TRACKED_AND_RUNNING:
case TRACKED_AND_LIVE:
showElement(watchRaceDiv);
if(race.startOfRace != null) {
@@ -175,16 +168,16 @@ public class EventRegattaRacesRace extends UIObject {
currentLegNo.setInnerText(String.valueOf(race.trackedRaceStatistics.currentLegNo));
totalLegsCount.setInnerText(String.valueOf(race.trackedRaceStatistics.totalLegsCount));
}
if(race.trackedRaceStatistics.hasLeaderData && race.trackedRaceStatistics.leaderOrWinner != null) {
if(race.trackedRaceStatistics.hasLeaderOrWinnerData && race.trackedRaceStatistics.leaderOrWinner != null) {
showElement(raceLeaderDiv);
updateWinnerOrLeader(raceLeader, race.trackedRaceStatistics.leaderOrWinner);
}
break;
case TRACKED_AND_FINISHED:
case TRACKED:
showElement(analyzeRaceDiv);
if(race.trackedRaceStatistics.hasLeaderData && race.trackedRaceStatistics.leaderOrWinner != null) {
if(race.trackedRaceStatistics.hasLeaderOrWinnerData && race.trackedRaceStatistics.leaderOrWinner != null) {
showElement(raceWinnerDiv);
updateWinnerOrLeader(raceWinner, race.trackedRaceStatistics.leaderOrWinner);
}
@@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Command;
@@ -220,11 +221,15 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
for (ComponentViewer componentViewer : componentViewers) {
mainPanel.add(componentViewer.getViewerWidget());
}
setLeaderboardVisible(getConfiguration().isShowLeaderboard());
boolean showLeaderboard = getConfiguration().isShowLeaderboard();
if (Document.get().getClientWidth() <= 1024) {
showLeaderboard = false;
}
setLeaderboardVisible(showLeaderboard);
setWindChartVisible(getConfiguration().isShowWindChart());
setCompetitorChartVisible(getConfiguration().isShowCompetitorsChart());
// make sure to load leaderboard data for filtering to work
if (!getConfiguration().isShowLeaderboard()) {
if (!showLeaderboard) {
leaderboardPanel.setVisible(true);
leaderboardPanel.setVisible(false);
}
@@ -9,6 +9,7 @@ import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
@@ -30,11 +31,21 @@ import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sse.common.Util.Pair;
/**
* Component Viewer that uses a {@link TouchSplitLayoutPanelWithBetterDraggers}
* Component Viewer that uses a {@link TouchSplitLayoutPanel}
* to display its components.
*
* TODO: Refactor to make sure it is really only performing operations
* that are related to view. Currently it is digging too deep into
* components and setting titles or even creating video buttons.
*/
public class SideBySideComponentViewer implements ComponentViewer {
private static final int DEFAULT_SOUTH_SPLIT_PANEL_HEIGHT = 200;
private final int MIN_LEADERBOARD_WIDTH = 428; // works well for 505 and ESS
/**
* Absolute Panel that informs its children about a resize
*/
class ResizableAbsolutePanel extends AbsolutePanel implements RequiresResize {
public void onResize() {
WidgetCollection children = getChildren();
@@ -46,10 +57,7 @@ public class SideBySideComponentViewer implements ComponentViewer {
}
}
private final int MIN_LEADERBOARD_WIDTH = 428;
private final LeaderboardPanel leftComponent;
private final Panel leaderboardContentPanel;
private final Component<?> rightComponent;
private final List<Component<?>> components;
private final ScrollPanel leftScrollPanel;
@@ -58,44 +66,62 @@ public class SideBySideComponentViewer implements ComponentViewer {
private LayoutPanel mainPanel;
private TouchSplitLayoutPanelWithBetterDraggers splitLayoutPanel;
private TouchSplitLayoutPanel splitLayoutPanel;
private int savedSplitPosition = -1;
private boolean layoutForLeftComponentForcedOnce = false;
public SideBySideComponentViewer(final LeaderboardPanel leftComponentP, final Component<?> rightComponentP, final MediaComponent mediaComponent, List<Component<?>> components, final StringMessages stringMessages) {
this.stringMessages = stringMessages;
this.leftComponent = leftComponentP;
this.leaderboardContentPanel = leftComponentP.getContentPanel();
this.rightComponent = rightComponentP;
this.components = components;
this.videoControlButton = createVideoControlButton(mediaComponent);
leftScrollPanel = new ScrollPanel();
leftScrollPanel.add(leftComponentP.getEntryWidget());
leftScrollPanel.setTitle(leftComponentP.getEntryWidget().getTitle());
mainPanel = new LayoutPanel();
mainPanel.setSize("100%", "100%");
mainPanel.getElement().getStyle().setMarginTop(-12, Unit.PX);
mainPanel.setStyleName("SideBySideComponentViewer-MainPanel");
splitLayoutPanel = new TouchSplitLayoutPanelWithBetterDraggers(/* horizontal splitter width */ 3, /*vertical splitter height*/ 25);
mainPanel.add(splitLayoutPanel);
this.leftScrollPanel = new ScrollPanel();
this.leftScrollPanel.add(leftComponentP.getEntryWidget());
this.leftScrollPanel.setTitle(leftComponentP.getEntryWidget().getTitle());
this.mainPanel = new LayoutPanel();
this.mainPanel.setSize("100%", "100%");
this.mainPanel.getElement().getStyle().setMarginTop(-12, Unit.PX);
this.mainPanel.setStyleName("SideBySideComponentViewer-MainPanel");
this.splitLayoutPanel = new TouchSplitLayoutPanel(/* horizontal splitter width */ 3, /*vertical splitter height*/ 25);
this.mainPanel.add(splitLayoutPanel);
// initialize components - they need to be added before other widgets to get the right width
initializeComponents();
// initialize the leaderboard component
savedSplitPosition = MIN_LEADERBOARD_WIDTH;
splitLayoutPanel.insert(leftScrollPanel, leftComponent, Direction.WEST, savedSplitPosition, null);
splitLayoutPanel.insert(leftScrollPanel, leftComponent, Direction.WEST, savedSplitPosition);
// create a panel that will contain the horizontal toggle buttons
ResizableAbsolutePanel panelForMapAndHorizontalToggleButtons = new ResizableAbsolutePanel();
panelForMapAndHorizontalToggleButtons.add(rightComponent.getEntryWidget());
splitLayoutPanel.insert(panelForMapAndHorizontalToggleButtons, rightComponent, Direction.CENTER, 0);
splitLayoutPanel.insert(panelForMapAndHorizontalToggleButtons, rightComponent, Direction.CENTER, 0, null);
// add additional toggle buttons panel that currently only contains the video button
List<Pair<Button, Component<?>>> additionalVerticalButtons = new ArrayList<Pair<Button,Component<?>>>();
additionalVerticalButtons.add(new Pair<Button, Component<?>>(videoControlButton, mediaComponent));
// ensure that toggle buttons are positioned right
splitLayoutPanel.lastComponentHasBeenAdded(this, panelForMapAndHorizontalToggleButtons, additionalVerticalButtons);
}
/**
* Create the video control button that shows or hides the video popup
*/
private Button createVideoControlButton(final MediaComponent mediaComponent) {
final Button videoControlButton = new Button(stringMessages.showVideoPopup());
final Button videoControlButton = new Button(new SafeHtml() {
private static final long serialVersionUID = 8679639887708833213L;
@Override
public String asString() {
if (Document.get().getClientWidth() <= 1024) {
return "&nbsp;";
} else {
return stringMessages.showVideoPopup();
}
}
});
videoControlButton.setTitle(stringMessages.showVideoPopup());
Button closeButton = new Button();
closeButton.setStyleName("VideoPopup-Close-Button");
final DialogBoxExt dialog = new DialogBoxExt(closeButton, stringMessages.videoComponentShortName(), /*modal*/false);
@@ -104,7 +130,9 @@ public class SideBySideComponentViewer implements ComponentViewer {
@Override
public void onClose(CloseEvent<PopupPanel> event) {
mediaComponent.setVisible(false);
videoControlButton.setText(stringMessages.showVideoPopup());
if (Document.get().getClientWidth() > 1024) {
videoControlButton.setText(stringMessages.showVideoPopup());
}
}
});
videoControlButton.addClickHandler(new ClickHandler() {
@@ -115,14 +143,18 @@ public class SideBySideComponentViewer implements ComponentViewer {
mediaComponent.setVisible(true);
dialog.setPopupPosition(10, Document.get().getClientHeight()-360);
dialog.show();
videoControlButton.setText(stringMessages.hideVideoPopup());
if (Document.get().getClientWidth() > 1024) {
videoControlButton.setText(stringMessages.hideVideoPopup());
}
} else {
Window.alert("This race has no default video associated.");
}
} else {
mediaComponent.setVisible(false);
dialog.hide();
videoControlButton.setText(stringMessages.showVideoPopup());
if (Document.get().getClientWidth() > 1024) {
videoControlButton.setText(stringMessages.showVideoPopup());
}
}
}
});
@@ -134,7 +166,7 @@ public class SideBySideComponentViewer implements ComponentViewer {
private void initializeComponents() {
for (final Component<?> component : components) {
splitLayoutPanel.insert(component.getEntryWidget(), component, Direction.SOUTH, 200, null);
splitLayoutPanel.insert(component.getEntryWidget(), component, Direction.SOUTH, 200);
}
}
@@ -148,21 +180,27 @@ public class SideBySideComponentViewer implements ComponentViewer {
return videoControlButton;
}
/**
* Called whenever the layout of {@link TouchSplitLayoutPanel} or other components
* change. Controls the visibility based on the {@link Component}s visibility.
* Each {@link Component} is in charge to not display any data or update itself
* when it is not visible.
*/
public void forceLayout() {
if (!leftComponent.isVisible() && rightComponent.isVisible()) {
// the leaderboard is not visible, but the map is
if (isWidgetInSplitPanel(leftScrollPanel)) {
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, leaderboardContentPanel, /*hidden*/true, savedSplitPosition);
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, /*hidden*/true, savedSplitPosition);
}
} else if (leftComponent.isVisible() && rightComponent.isVisible()) {
// the leaderboard and the map are visible
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, leaderboardContentPanel, /*hidden*/false, savedSplitPosition);
splitLayoutPanel.setWidgetVisibility(leftScrollPanel, leftComponent, /*hidden*/false, savedSplitPosition);
} else if (!leftComponent.isVisible() && !rightComponent.isVisible()) {
}
for (Component<?> component : components) {
final boolean isComponentVisible = component.isVisible();
splitLayoutPanel.setWidgetVisibility(component.getEntryWidget(), component, component.getEntryWidget(), !isComponentVisible, 200);
splitLayoutPanel.setWidgetVisibility(component.getEntryWidget(), component, !isComponentVisible, DEFAULT_SOUTH_SPLIT_PANEL_HEIGHT);
}
splitLayoutPanel.forceLayout();
}
@@ -46,7 +46,24 @@ import com.sap.sse.common.Util.Pair;
import com.sap.sse.gwt.client.player.TimeListener;
/**
* A panel that adds user-positioned splitters between each of its child widgets.
* A panel that adds user-positioned splitters between each of its child widgets. These splitters have draggers
* that are designed that way, that they work on touch enabled devices. Splitters can be hidden or shown.
* Each dragger has a toggle button associated that shows or hides the splitter it is associated to.
*
* <p>
* Splitters have a {@link Widget} and a {@link Component} associated. The widget is applied to the split panel,
* the component is being used to determine the visibility.
* </p>
*
* <p>
* Make sure that you insert {@link Widget}s only by using {@link #insert(Widget, Component, Direction, double)}.
* The {@link Widget} provides the title for the toggle button and the {@link Component} provides the visibility.
* </p>
*
* <p>
* For each splitter the visibility is determined by checking its {@link Component}s visibility. If the associated
* component is not visible then the splitter will hide itself.
* </p>
*
* <p>
* This panel is used in the same way as {@link DockLayoutPanel}, except that its children's sizes are always specified
@@ -65,19 +82,25 @@ import com.sap.sse.gwt.client.player.TimeListener;
* <li>.gwt-SplitLayoutPanel .gwt-SplitLayoutPanel-VDragger { vertical dragger }</li>
* </ul>
*
* <p>
* <h3>Example</h3>
* {@example com.google.gwt.examples.SplitLayoutPanelExample}
* </p>
* @author Simon Marcel Pamies, Axel Uhl
*/
public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
public class TouchSplitLayoutPanel extends DockLayoutPanel {
/**
* A dragger that is connected to a splitter. It captures
* mouse events and changes the size of the associated
* {@link Splitter} once the mouse has been released. It is designed
* in a way to support touch events.
*
* @author Simon Marcel Pamies, Axel Uhl
*/
public class Dragger extends Widget {
private final Splitter associatedSplitter;
public Dragger(Splitter associatedSplitter) {
this.associatedSplitter = associatedSplitter;
this.setElement(Document.get().createDivElement());
// needed to make sure this custom element gets events
this.sinkEvents(Event.ONMOUSEDOWN | Event.ONMOUSEUP | Event.ONMOUSEMOVE | Event.ONDBLCLICK
| Event.ONTOUCHSTART | Event.ONTOUCHEND | Event.ONTOUCHMOVE);
}
@@ -158,6 +181,25 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
}
}
/**
* <p>
* Splitter that has a {@link Dragger} associated that
* controls the size of this widget. Each splitter also has a
* {@link Widget} associated that is under its direct control.
* A splitter can be hidden or visible depending on the associated
* {@link Component}s state.
* </p>
*
* <p>
* Splitters can also hold a panel that contains {@link Button}s that
* toggle all Splitters in the same direction. The idea is that if only
* one splitter is visible one wants to still display all other splitter
* toggle buttons. The logic to move that panel to the right splitter
* is contained in {@link TouchSplitLayoutPanel}.
* </p>
*
* @author Simon Marcel Pamies, Axel Uhl
*/
abstract class Splitter extends AbsolutePanel {
protected final Widget target;
@@ -182,7 +224,11 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
private Panel toggleButtonsPanel;
/**
* A splitter is an {@link AbsolutePanel} that contains a horizontal line and a dragger
* A splitter is an {@link AbsolutePanel} that contains a horizontal line and a dragger.
*
* @param reverse if set to true the size change during {@link Dragger} operations will
* be reversed. That means that when dragging to top for a {@link VSplitter}
* the splitters height will get less.
*/
public Splitter(Widget target, Component<?> associatedComponent, boolean reverse) {
super();
@@ -257,18 +303,6 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
this.toggleDisplayAllowed = allowed;
}
protected abstract int getAbsolutePosition();
protected abstract double getCenterSize();
protected abstract int getEventPosition(Event event);
protected abstract int getTargetPosition();
protected abstract int getTargetSize();
protected abstract int getSplitterSize();
protected void setSplitterSize(double splitterSize) {
LayoutData layout = (LayoutData)getLayoutData();
layout.size = splitterSize;
@@ -287,6 +321,13 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
return Math.max(((LayoutData) target.getLayoutData()).size + centerSize, 0);
}
/**
* Sets the size of the associated {@link Widget} thus moving it down, up, left or right
* depending of the position. Informs the {@link DockLayoutPanel} about the change.
*
* @param defer if set to true then the layout change will be deferred until the browser
* event loop returns.
*/
private void setAssociatedWidgetSize(double size, boolean defer) {
double maxSize = getMaxSize();
if (size > maxSize) {
@@ -320,6 +361,7 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
};
Scheduler.get().scheduleDeferred(layoutCommand);
} else {
// force layout on DockPanel
forceLayout();
}
if (getAssociatedComponent() instanceof RequiresResize) {
@@ -331,8 +373,22 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
});
}
}
protected abstract int getAbsolutePosition();
protected abstract double getCenterSize();
protected abstract int getEventPosition(Event event);
protected abstract int getTargetPosition();
protected abstract int getTargetSize();
protected abstract int getSplitterSize();
}
/**
* Horizontal {@link Splitter} implementation that will only work when
* set to {@link Direction#EAST} or {@link Direction#WEST}.
*
* @author Simon Marcel Pamies, Axel Uhl
*/
class HSplitter extends Splitter {
public HSplitter(Widget target, Component<?> associatedComponent, boolean reverse) {
super(target, associatedComponent, reverse);
@@ -380,6 +436,12 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
}
/**
* Vertical Splitter that will only work when applied to
* {@link Direction#NORTH} or {@link Direction#SOUTH}.
*
* @author Simon Marcel Pamies, Axel Uhl
*/
class VSplitter extends Splitter {
public VSplitter(final Widget target, final Component<?> associatedComponent, boolean reverse) {
super(target, associatedComponent, reverse);
@@ -438,19 +500,18 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
private final int verticalSplitterSize;
/**
* Construct a new {@link TouchSplitLayoutPanelWithBetterDraggers} with the default splitter size of 8px.
* Construct a new {@link TouchSplitLayoutPanel} with the default splitter size of 8px.
*/
public TouchSplitLayoutPanelWithBetterDraggers() {
public TouchSplitLayoutPanel() {
this(DEFAULT_SPLITTER_SIZE, DEFAULT_SPLITTER_SIZE);
}
/**
* Construct a new {@link TouchSplitLayoutPanelWithBetterDraggers} with the specified splitter size in pixels.
* Construct a new {@link TouchSplitLayoutPanel} with the specified splitter sizes in pixels.
* {@link Splitter}s can be of different sizes.
*
* @param splitterSize
* the size of the splitter in pixels
*/
public TouchSplitLayoutPanelWithBetterDraggers(int horizonatalSplitterSize, int verticalSplitterSize) {
public TouchSplitLayoutPanel(int horizonatalSplitterSize, int verticalSplitterSize) {
super(Unit.PX);
this.horizontalSplitterSize = horizonatalSplitterSize;
this.verticalSplitterSize = verticalSplitterSize;
@@ -487,26 +548,57 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
}
@Override
/**
* This method should never be used directly as the implementation requires
* a Component to be available.
*/
public void insert(Widget child, Direction direction, double size, Widget before) {
this.insert(child, null, direction, size, before);
this.insert(child, null, direction, size);
}
public void insert(Widget child, Component<?> associatedComponent, Direction direction, double size, Widget before) {
super.insert(child, direction, size, before);
/**
* Inserts a new child to this panel. When using this method make absolutely sure to
* call {@link #lastComponentHasBeenAdded(SideBySideComponentViewer, AbsolutePanel, List)} after
* all components have been added.
*
* @param child the child {@link Widget} that will be added
* @param associatedComponent the {@link Component} that will be used to determine visibility
* @param size the size of the widget. This will define the size of the split panel.
*/
public void insert(Widget child, Component<?> associatedComponent, Direction direction, double size) {
super.insert(child, direction, size, null);
if (direction != Direction.CENTER) {
super.insert(child, direction, size, before);
insertSplitter(child, before, associatedComponent);
super.insert(child, direction, size, null);
insertSplitter(child, associatedComponent);
LayoutData widgetLayoutData = (LayoutData) child.getLayoutData();
widgetLayoutData.oldSize = size;
}
}
public void lastComponentHasBeenAdded(final SideBySideComponentViewer cm, AbsolutePanel panelForAdditionalButtons, List<Pair<Button, Component<?>>> additionalVerticalButtons) {
/**
* <p>
* This method makes sure to layout the toggle buttons right. It needs to be the last call
* after all widgets have been added by using {@link #insert(Widget, Component, Direction, double)}.
* Be aware of the fact that one needs to provide the panel for the horizontal buttons.
* </p>
*
* <p>
* With this call one can provide additional vertical toggle buttons that won't be related
* to any {@link Splitter} but will be under control of the algorithm that makes sure that
* all toggle buttons are positioned right.
* </p>
*
* @param cm the component that will be informed about layout changes by calling forceLayout().
* @param panelForHorizontalButtons this panel assumes that the horizontal buttons will be positioned
* outside of the horizontal splitter panel. In most cases one will create such a panel
* on top of the widget that has position {@link Direction#CENTER}.
*/
public void lastComponentHasBeenAdded(final SideBySideComponentViewer cm, AbsolutePanel panelForHorizontalButtons, List<Pair<Button, Component<?>>> additionalVerticalButtons) {
WidgetCollection splitterChildren = super.getChildren();
HSplitter lastHorizontalSplitter = null;
VSplitter lastVerticalSplitter = null;
List<Splitter> allVerticalSplitters = new ArrayList<TouchSplitLayoutPanelWithBetterDraggers.Splitter>();
List<Splitter> allHorizontalSplitters = new ArrayList<TouchSplitLayoutPanelWithBetterDraggers.Splitter>();
List<Splitter> allVerticalSplitters = new ArrayList<TouchSplitLayoutPanel.Splitter>();
List<Splitter> allHorizontalSplitters = new ArrayList<TouchSplitLayoutPanel.Splitter>();
for (Widget widget : splitterChildren) {
if (widget instanceof VSplitter) {
allVerticalSplitters.add((VSplitter) widget);
@@ -530,12 +622,16 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
if (lastHorizontalSplitter != null) {
Panel horizontalButtonsPanel = createToggleButtonPanel(allHorizontalSplitters,
"gwt-SplitLayoutPanel-EastToggleButton-Panel", "gwt-SplitLayoutPanel-EastToggleButton", cm, null);
panelForAdditionalButtons.add(horizontalButtonsPanel);
panelForHorizontalButtons.add(horizontalButtonsPanel);
lastHorizontalSplitter.setVisible(false);
lastHorizontalSplitter.setDraggerVisible(false);
}
}
/**
* Create toggle buttons and their click handlers. The click handlers will show or hide associated
* {@link Splitter}s including the {@link Component} and {@link Widget}.
*/
private Panel createToggleButtonPanel(List<Splitter> splitters, String panelStyleName, String buttonStyleName,
final SideBySideComponentViewer componentViewer, List<Pair<Button, Component<?>>> additionalButtons) {
FlowPanel buttonFlowPanel = new FlowPanel();
@@ -545,6 +641,9 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
Button button = buttonAndComponentPair.getA();
button.setStyleName(buttonStyleName);
button.addStyleDependentName("Closed-"+buttonAndComponentPair.getB().getDependentCssClassName());
if (Document.get().getClientWidth() <= 1024) {
button.addStyleDependentName("Small-"+buttonAndComponentPair.getB().getDependentCssClassName());
}
buttonFlowPanel.add(button);
}
}
@@ -586,10 +685,14 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
return buttonFlowPanel;
}
/**
* This method ensures that the toggle buttons always are positioned on top of all split panels. This requires
* a bit of trickery as there are some edge cases (e.g. when no splitter is visible at all).
*/
private void ensureVerticalToggleButtonPosition() {
List<VSplitter> splitterVisibleAndComponentVisible = new ArrayList<TouchSplitLayoutPanelWithBetterDraggers.VSplitter>();
List<VSplitter> splitterVisibleButComponentnNotVisible = new ArrayList<TouchSplitLayoutPanelWithBetterDraggers.VSplitter>();
List<VSplitter> splitterInvisible = new ArrayList<TouchSplitLayoutPanelWithBetterDraggers.VSplitter>();
List<VSplitter> splitterVisibleAndComponentVisible = new ArrayList<TouchSplitLayoutPanel.VSplitter>();
List<VSplitter> splitterVisibleButComponentnNotVisible = new ArrayList<TouchSplitLayoutPanel.VSplitter>();
List<VSplitter> splitterInvisible = new ArrayList<TouchSplitLayoutPanel.VSplitter>();
VSplitter splitterWithToggleButtons = null;
for (Widget widget : super.getChildren()) {
if (widget instanceof VSplitter) {
@@ -692,8 +795,23 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
}
}
/**
* <p>
* Sets the widget visibility and makes sure to also hide or show associated
* {@link Splitter}s and their {@link Dragger}s. Also control the associated
* toggle button by setting the correct style name.
* </p>
*
* <p>
* As this method has no knowledge about the connection between a {@link Widget} and
* a {@link Component} the component needs to be provided.
* </p>
*
* @param hidden set this to true if the panel should be hidden
* @param size the size of the panel. Always provide the visible size even when hidden is true.
*/
public void setWidgetVisibility(Widget widget, Component<?> associatedComponentToWidget,
final Widget widgetThatDeterminesSize, final boolean hidden, final int size) {
final boolean hidden, final int size) {
super.setWidgetHidden(widget, hidden);
final Splitter splitter = getAssociatedSplitter(widget);
if (splitter != null) {
@@ -804,7 +922,7 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
return null;
}
private Splitter insertSplitter(Widget widget, Widget before, Component<?> associatedComponent) {
private Splitter insertSplitter(Widget widget, Component<?> associatedComponent) {
assert getChildren().size() > 0 : "Can't add a splitter before any children";
LayoutData layout = (LayoutData) widget.getLayoutData();
@@ -825,7 +943,8 @@ public class TouchSplitLayoutPanelWithBetterDraggers extends DockLayoutPanel {
default:
assert false : "Unexpected direction";
}
super.insert(splitter, layout.direction, splitter.getSplitterSize(), before);
super.insert(splitter, layout.direction, splitter.getSplitterSize(), null);
// this is needed to make the Draggers visible
((LayoutData) splitter.getLayoutData()).layer.getContainerElement().getStyle().setOverflow(Overflow.VISIBLE);
return splitter;
}
@@ -2220,7 +2220,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
raceDTO = baseDomainFactory.createRaceDTO(getService(), withGeoLocationData, raceIdentifier, trackedRace);
if(withStatisticalData) {
Collection<MediaTrack> mediaTracksForRace = getService().getMediaTracksForRace(raceIdentifier);
raceDTO.trackedRaceStatistics = baseDomainFactory.createTrackedRaceStatisticsDTO(trackedRace, mediaTracksForRace);
raceDTO.trackedRaceStatistics = baseDomainFactory.createTrackedRaceStatisticsDTO(trackedRace, leaderboard, raceColumn, fleet, mediaTracksForRace);
}
}
final FleetDTO fleetDTO = baseDomainFactory.convertToFleetDTO(fleet);
+2
View File
@@ -4,6 +4,8 @@
We run two servers for the event, A and B, for fast and available switching. The DB they use is called 505WORLDS2014, and so is the replication channel. The git branch we use to merge the features we want to deploy to the event servers is called `505worlds2014` and the git tag used to build a release for it is called `505worlds2014-release`. See http://hudson.sapsailing.com/job/SAPSailingAnalytics-505worlds2014/ and http://hudson.sapsailing.com/job/SAPSailingAnalytics-505worlds2014-release/, respectively, for their Hudson jobs.
RabbitMQ server for replication: 54.246.250.138
Regatta Overview: http://505worlds2014ro.sapsailing.com
Manage2Sail Event URL: http://manage2sail.com/api/public/links/event/d70bee3e-9f65-46f4-96a1-9fd467712ce0?accesstoken=bDAv8CwsTM94ujZ&mediaType=json
+9
View File
@@ -0,0 +1,9 @@
# Profiling
Use the SAP JVM 7 available at http://sapjvm.wdf.sap.corp:1080 and its Eclipse profiler plug-in available from the same location. The update site to add to your Eclipse is http://sapjvm.wdf.sap.corp:1080/profiling. After unpacking the VM, make it known by your Eclipse installation and use it for the launch configuration that you want to profile.
## Profiling GWT Client Code
Using GWT's DevMode, you can also profile GWT Java code running in the client. You have to keep in mind that many things work differently when in DevMode and that significant overhead for the Browser-to-JavaVM communication is generated. However, your core algorithms that require attention performance-wise will often still stick out, assuming some clever filtering in the Eclipse profiler plug-in which is easily possible.
For GWT profiling please note that there is no immediate support to launch an SAP JVM in profiling mode for a GWT launch config. Instead, use the `Sailing GWT Remote Profiling (Run-Mode only)` launch configuration in Run mode. It will pause at start-up and wait for the profiler to get connected. Then use the profiling launch configuration `Profile SailingGWT` to get started.