Merge branch 'master' into bug3529

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceTableWrapper.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupPanel.java
This commit is contained in:
Steffen Schaefer
2017-04-06 13:19:12 +02:00
42 changed files with 857 additions and 112 deletions
@@ -1,5 +1,3 @@
@CHARSET "UTF-8";
.cellTableHeader {
padding: 7px;
padding-left: 10px;
@@ -9,4 +9,7 @@
<source path='common/scalablevalue'/>
<source path='common/racelog/tracking'/>
<source path='common/scalablevalue/impl'/>
<!-- Remove Classes that fit to the remote interfaces but only cause warnings and never could be serialized anyway -->
<extend-configuration-property name="rpc.blacklist" value="com.sap.sailing.domain.common.quadtree.QuadTree"/>
<extend-configuration-property name="rpc.blacklist" value="com.google.gwt.validation.client.impl.PathImpl"/>
</module>
@@ -22,7 +22,6 @@ public class CompetitorDescriptor extends NamedImpl implements Named {
private final String raceName;
private final String fleetName;
private final String sailNumber;
private final String name;
private final CountryCode countryCode;
private final List<PersonDTO> persons;
private final Double timeOnTimeFactor;
@@ -39,7 +38,6 @@ public class CompetitorDescriptor extends NamedImpl implements Named {
this.raceName = raceName;
this.fleetName = fleetName;
this.sailNumber = sailNumber;
this.name = name;
this.teamName = teamName;
this.countryCode = countryCode;
this.persons = new ArrayList<>();
@@ -72,10 +70,6 @@ public class CompetitorDescriptor extends NamedImpl implements Named {
return sailNumber;
}
public String getName() {
return name;
}
public CountryCode getCountryCode() {
return countryCode;
}
@@ -104,7 +98,8 @@ public class CompetitorDescriptor extends NamedImpl implements Named {
public String toString() {
return "CompetitorDescriptor [eventName=" + eventName + ", regattaName=" + regattaName + ", boatClassName="
+ boatClassName + ", boatName=" + boatName + ", teamName=" + teamName + ", raceName=" + raceName
+ ", fleetName=" + fleetName + ", sailNumber=" + sailNumber + ", name=" + name + ", countryCode="
+ ", fleetName=" + fleetName + ", sailNumber=" + sailNumber + ", name=" + super.getName()
+ ", countryCode="
+ countryCode + ", persons=" + persons + ", timeOnTimeFactor=" + timeOnTimeFactor
+ ", timeOnDistanceAllowancePerNauticalMile=" + timeOnDistanceAllowancePerNauticalMile + "]";
}
@@ -0,0 +1,46 @@
package com.google.gwt.user.client.rpc.core.com.sap.sse.common.impl;
import com.google.gwt.user.client.rpc.CustomFieldSerializer;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.client.rpc.SerializationStreamReader;
import com.google.gwt.user.client.rpc.SerializationStreamWriter;
import com.sap.sse.common.impl.NamedImpl;
public class NamedImpl_CustomFieldSerializer extends CustomFieldSerializer<NamedImpl> {
@Override
public void serializeInstance(SerializationStreamWriter streamWriter, NamedImpl instance)
throws SerializationException {
serialize(streamWriter, instance);
}
public static void serialize(SerializationStreamWriter streamWriter, NamedImpl instance)
throws SerializationException {
streamWriter.writeString(instance.getName());
}
@Override
public boolean hasCustomInstantiateInstance() {
return true;
}
@Override
public NamedImpl instantiateInstance(SerializationStreamReader streamReader) throws SerializationException {
return instantiate(streamReader);
}
public static NamedImpl instantiate(SerializationStreamReader streamReader) throws SerializationException {
return new NamedImpl(streamReader.readString());
}
@Override
public void deserializeInstance(SerializationStreamReader streamReader, NamedImpl instance)
throws SerializationException {
deserialize(streamReader, instance);
}
public static void deserialize(SerializationStreamReader streamReader, NamedImpl instance) {
// Done by instantiateInstance
}
}
@@ -56,7 +56,7 @@
class="{local_res.css.sitefooter_links_link} {res.mediaCss.showonsmall} {res.mediaCss.hideonmedium} {res.mediaCss.hideonlarge}">
Mobile
</a>
<a href="{releaseNotesNavigation.getTargetUrl}" class="{local_res.css.sitefooter_links_link}">
<a href="{releaseNotesNavigation.getSafeTargetUrl}" class="{local_res.css.sitefooter_links_link}">
<ui:text from='{i18n.whatsNew}' />
</a>
</div>
@@ -9,6 +9,8 @@ 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.safehtml.shared.SafeUri;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
@@ -30,7 +32,7 @@ public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends Abstr
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);
SafeHtml standaloneButton(String styleNames, String iconStyleNames, String text, String icon, SafeUri safeUri);
}
private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class);
@@ -81,7 +83,8 @@ public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends Abstr
if (data.hasValidTrackingData()) {
if (renderAsDirectLinkButton(data)) {
sb.append(TEMPLATE.standaloneButton(plannedStyleNames, iconStyleNames, I18N.watchLive(),
"launch-play", presenter.getRaceViewerURL(data, RaceBoardModes.PLAYER.name())));
"launch-play",
UriUtils.fromString(presenter.getRaceViewerURL(data, RaceBoardModes.PLAYER.name()))));
} else {
String styleNames = data.isFinished() ? analyzeStyleNames : liveStyleNames;
sb.append(TEMPLATE.raceviewerLaunchPad(styleNames, iconStyleNames, I18N.raceDetailsToShow()));
@@ -2,8 +2,11 @@ package com.sap.sailing.gwt.home.desktop.partials.solutions;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
@@ -20,6 +23,7 @@ import com.sap.sailing.gwt.home.desktop.places.whatsnew.WhatsNewPlace.WhatsNewNa
import com.sap.sailing.gwt.home.shared.app.PlaceNavigation;
import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace;
import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace.SolutionsNavigationTabs;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class Solutions extends Composite {
interface SolutionsUiBinder extends UiBinder<Widget, Solutions> {
@@ -29,6 +33,7 @@ public class Solutions extends Composite {
private static final HyperlinkImpl HYPERLINK_IMPL = GWT.create(HyperlinkImpl.class);
@UiField StringMessages i18n;
@UiField Anchor sailingAnalyticsAnchor;
@UiField Anchor raceAnchor;
@UiField Anchor inSightAnchor;
@@ -44,13 +49,23 @@ public class Solutions extends Composite {
@UiField DivElement postRaceDiv;
@UiField DivElement simulatorDiv;
@UiField
Anchor sapInSailingAnchor;
@UiField Anchor sapInSailingAnchor;
@UiField Anchor sailingAnalyticsDetailsAnchor;
@UiField Anchor raceCommitteeAppDetailsAnchor;
@UiField Anchor inSightAppDetailsAnchor;
@UiField Anchor buoyPingerAppDetailsAnchor;
@UiField Anchor simulatorAppDetailsAnchor;
@UiField AnchorElement raceManagerPlayStoreLinkUi;
@UiField AnchorElement inSightAppStoreLinkUi;
@UiField AnchorElement buoyPingerPlayStoreLinkUi;
@UiField
AnchorElement inSightPlayStoreLinkUi;
@UiField ImageElement raceManagerPlayStoreImgUi;
@UiField ImageElement inSightPlayStoreImgUi;
@UiField ImageElement buoyPingerPlayStoreImgUi;
@UiField ImageElement inSightAppStoreImgUi;
private final PlaceNavigation<SolutionsPlace> sapInSailingNavigation;
private final PlaceNavigation<SolutionsPlace> sailingAnalyticsNavigation;
@@ -77,6 +92,20 @@ public class Solutions extends Composite {
initWidget(uiBinder.createAndBindUi(this));
String playstorebadgeSrc = UriUtils.fromString(i18n.playstoreBadge()).asString();
String insightAppstoreSrc = UriUtils.fromString("images/home/appstore" + i18n.appstoreBadgeSuffix() + ".svg")
.asString();
raceManagerPlayStoreLinkUi.setHref(UriUtils.fromString(i18n.playstoreRacecommitteeApp()));
inSightAppStoreLinkUi.setHref(UriUtils.fromString(i18n.appstoreSapSailInsight()));
buoyPingerPlayStoreLinkUi.setHref(UriUtils.fromString(i18n.playStoreBuoyPingerApp()));
inSightPlayStoreLinkUi.setHref(UriUtils.fromString(i18n.playstoreInsightApp()));
raceManagerPlayStoreImgUi.setSrc(playstorebadgeSrc);
inSightPlayStoreImgUi.setSrc(playstorebadgeSrc);
buoyPingerPlayStoreImgUi.setSrc(playstorebadgeSrc);
inSightAppStoreImgUi.setSrc(insightAppstoreSrc);
sailingAnalyticsDetailsNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingAnalytics);
raceCommitteeAppDetailsNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.RaceManagerApp);
inSightAppDetailsNavigation = placesNavigator.getWhatsNewNavigation(WhatsNewNavigationTabs.InSightApp);
@@ -47,7 +47,6 @@
</div>
</div>
<div class="{local_res.css.solutions}">
<div ui:field="sapInSailingDiv"
class="{local_res.css.solutions_content} {local_res.css.solutions_contentsapinsailing}">
@@ -138,13 +137,10 @@
class="{style.storeBadgesContainer}"
>
<a ui:field="raceManagerPlayStoreLinkUi"
href="{i18n.playstoreRacecommitteeApp}"
target="_blank"
><img
ui:field="raceManagerPlayStoreImgUi"
alt="Get it on Google Play"
src="{i18n.playstoreBadge}"
/></a>
alt="Get it on Google Play" /></a>
</div>
<g:Anchor ui:field="raceCommitteeAppDetailsAnchor"
styleName="{res.mainCss.button}">
@@ -167,23 +163,19 @@
</p>
<div class="{style.storeBadgesContainer}">
<a ui:field="inSightAppStoreLinkUi"
href="{i18n.appstoreSapSailInsight}"
target="_blank"
><img
ui:field="inSightAppStoreImgUi"
alt="Get it on AppStore"
src="images/home/appstore{i18n.appstoreBadgeSuffix}.svg"
/></a>
</div>
<div class="{style.storeBadgesContainer}">
<a ui:field="inSightPlayStoreLinkUi"
href="{i18n.playstoreInsightApp}"
target="_blank"
><img
ui:field="inSightPlayStoreImgUi"
alt="Get it on Google Play"
src="{i18n.playstoreBadge}"
/></a>
/></a>
</div>
<g:Anchor ui:field="inSightAppDetailsAnchor"
styleName="{res.mainCss.button}">
@@ -208,12 +200,10 @@
class="{style.storeBadgesContainer}"
>
<a ui:field="buoyPingerPlayStoreLinkUi"
href="{i18n.playStoreBuoyPingerApp}"
target="_blank"
><img
ui:field="buoyPingerPlayStoreImgUi"
alt="Get it on Google Play"
src="{i18n.playstoreBadge}"
/></a>
</div>
<g:Anchor ui:field="buoyPingerAppDetailsAnchor"
@@ -8,22 +8,22 @@ import com.google.gwt.resources.client.TextResource;
public interface WhatsNewResources extends ClientBundle {
public static final WhatsNewResources INSTANCE = GWT.create(WhatsNewResources.class);
@Source("com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html")
@Source("resources/SailingAnalyticsNotes.html")
public TextResource getSailingAnalyticsNotesHtml();
@Source("com/sap/sailing/gwt/home/solutions/SailingSimulatorNotes.html")
@Source("resources/SailingSimulatorNotes.html")
public TextResource getSailingSimulatorNotesHtml();
@Source("com/sap/sailing/gwt/home/solutions/RaceCommitteeAppNotes.html")
@Source("resources/RaceCommitteeAppNotes.html")
public TextResource getRaceCommitteeAppNotesHtml();
@Source("com/sap/sailing/gwt/home/solutions/InSightAppNotes.html")
@Source("resources/InSightAppNotes.html")
public TextResource getInSightAppNotesHtml();
@Source("com/sap/sailing/gwt/home/solutions/BuoyPingerAppNotes.html")
@Source("resources/BuoyPingerAppNotes.html")
public TextResource getBuoyPingerAppNotesHtml();
@Source("WhatsNew.css")
@Source("resources/WhatsNew.css")
LocalCss css();
public interface LocalCss extends CssResource {
@@ -0,0 +1,575 @@
<!DOCTYPE html>
<html>
<body>
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent">
<h5 class="articleSubheadline">February 2017</h5>
<ul class="bulletList">
<li>The definition of "ride height" for foiling sensors has been changed. Instead of showing
the ride height calculated by the sensor we are now showing the minimum of the port and
starboard ride heights. Therefore, this new definition of ride height can be seen as the
distance of lowest hull part from the water.
</li>
</ul>
<h5 class="articleSubheadline">January 2017</h5>
<ul class="bulletList">
<li>An issue with wind-up map rotation which sometimes left the map centered over the African continent has been fixed.
</li>
</ul>
<h5 class="articleSubheadline">October 2016</h5>
<ul class="bulletList">
<li>An issue with live re-calculation of the maximum speed over ground (SOG) values has been fixed.
In some circumstances it was possible that new live data did not properly update the MaxSOG figures.
Since this depended on the timing of read and write access it could have led to different values
for different server instances, giving confusing results. The fix provided now ensures that
the MaxSOG values are properly evaluated when new data has arrived.
</li>
<li>The buoy zone radius is now determined by a configurable regatta property. By default, it is
set to two hull lengths, and a regatta administrator may choose to change it to any other factor
such as 3 or 2.5. This determines the default used in the race viewer (RaceBoard.html). Users can
still change the radius in the map settings to any arbitrary number, deviating from what has
"officially" been set for the regatta.
</li>
</ul>
<h5 class="articleSubheadline">August 2016</h5>
<ul class="bulletList">
<li>In order to prepare user-specific configuration support in additional parts of the application,
various modules now require a user account to be accessed. Please sign up and log in to use their
full functionality as usual. The following modules are affected:
<ul>
<li><tt>/gwt/Simulator.html</tt></li>
<li><tt>/gwt/DataMining.html</tt></li>
</ul>
</li>
</ul>
<h5 class="articleSubheadline">July 2016</h5>
<ul class="bulletList">
<li>The Wind arrow in the top left corner of the race map / player now serves as a toggle button
for the wind streamlet overlay which can still also be reached through the map settings.
</li>
<li>If you want to analyze a race, the navigation slightly changed. The button, which is shown for tracked
races won't lead you to the race board directly, but provides a small menu. There you can choose from
several pre-defined analysis, which will launch the race board with respective configurations:
<ul>
<li>Replay/Watch live: Launches race board in play mode starting at race start, if race is already
finished (Replay) or at the actual time, if the race is currently live (Watch live)
</li>
<li>Race analysis: Shows the race results by setting the timer to the end of the race.
<i>This option is only available, if the race is already finished.</i>
</li>
<li>Start analysis: Shows the race start 10 sec after start and shows the speed over ground (SOG) of
the best starters within the competitor chart (zoomed to start).
<i>This option is only available, if the race is already started.</i>
</li>
<li>Winning lanes: Shows the full lanes of the best competitor, if race is already finished or the of
leading competitors if the race is currently live.
</li>
</ul>
</li>
<li>On the user profile page you now can maintain a list of you favorite boat classes and competitors.
You can also opt in for notification mails about your favorites to stay informed.
A requirement for notification mails to work for you is that you entered a valid mail address
to your user profile and verified the address by clicking the link that has been sent via email.
</li>
<li>Users now may choose their preferred language on the user profile page.
Currently only the mail notification mechanism will accept this setting to send mails in the preferred
language. This also prepares additional language sensitive feature, e.g. automatically switching the
website to your preferred language when logged in.
</li>
<li>The leaderboard now has a new measure value called "Ride height", which shows the ride height on
foils of a competitor, if respective data is available (currently ESS only). This information can
also be shown in the competitor chart on the RaceBaord page.
</li>
<li>To visualize multiple information at once, the competitor chart on the RaceBaord page can now be
configured to show two Y-axes.
</li>
</ul>
<h5 class="articleSubheadline">May 2016</h5>
<ul class="bulletList">
<li>It is now possible to follow races on the RaceBoard, even if there's no wind data available.</li>
</ul>
<h5 class="articleSubheadline">April 2016</h5>
<ul class="bulletList">
<li>The <tt>/gwt/RaceBoard.html</tt> entry point now accepts an optional parameter "t" in the form
<tt>[[hh:]mm:]ss</tt>, for example <tt>t=12:34</tt> to place the timer so much after the race start
if the viewer comes up in Replay mode. You may also use just seconds, as in <tt>t=1234</tt> which
makes specifying a time <em>before</em> the start somewhat easier (<tt>t=-123</tt> instead of
<tt>t=-02:-03</tt>).
</li>
</ul>
<h5 class="articleSubheadline">February 2016</h5>
<ul class="bulletList">
<li>The website now features a user registration and authentication interface.
User accounts are currently used to grant special permissions to administrators and moderators.
New user specific features (e.g. preferences) will be available in the next couple of months.
</li>
<li>The RaceBoard URL Parameter "canReplayDuringLiveRaces" has been removed.
This parameter was used to allow users to trigger autoplay for live races on non-live points in time.
The permission "CAN_REPLAY_DURING_LIVE_RACES" and role "moderator" now grant this functionality to users.
</li>
<li>Mark positions are no longer interpolated between fixes. Instead, when a mark's track is asked for the
mark's position, the last known position at or before the time point requested will be used. The
"jumping" of marks in the UI that may otherwise be caused by this is ameliorated by introducing
the same CSS animation for the marks that is already used for the boats.
</li>
</ul>
<h5 class="articleSubheadline">January 2016</h5>
<ul class="bulletList">
<li>Show maneuver markers on the race board by default
(tacks, jibes and penalty circles) with a smaller icon, which
shows an info bubble when the marker is clicked.
The icon turns green if the direction change is to starboard
and red if to port.
</li>
</ul>
<h5 class="articleSubheadline">December 2015</h5>
<ul class="bulletList">
<li>The advantage line is now animated and moves smoothly with the boats.
</li>
<li>New line hover behavior in race map. When the mouse is moved onto a line, for example the start line, a transparent,
wider line is activated behind it that acts as a buffer for the thin line, where the text displayed by the hover is still shown.
This makes it easier to hover over the lines. These wider lines can be made semi transparent instead of transparent
by unticking the corresponding checkbox in the settings of the race map. Furthermore the stroke weight of the wider lines can be
configured in the same settings dialog.
</li>
<li>The race viewer (RaceBoard.html) now intelligently chooses a layout that suits the screen size.
When only little horizontal space is available, only the sail number and nationality but not the
competitor's long name is displayed by default. When vertical space is scarce, competitor and wind
charts are disabled and more horizontal space can be given to the time slider at the bottom. The
leaderboard section can then be scaled to smaller widths and will reduce the column widths better
than in the past.
</li>
<li>With the changes described above, the RaceBoard is now also accessible from the races page on mobile devices.
</li>
<li>For overall / series leaderboards such as a national sailing league or the Extreme Sailing Series, the
live status of the overall leaderboard is now indicated. As long as a live race is ongoing, the respective
column in the overall leaderboard is highlighted, and a comment is displayed that indicates which regatta
is currently still live.
</li>
</ul>
<h5 class="articleSubheadline">November 2015</h5>
<ul class="bulletList">
<li>The smartphone page provides some structural navigation items for several pages in the upper right burger menu.
You will see links to e.g. events, regattas and series when navigating on event and series pages.
</li>
<li>It is now possible to filter for competitors in the race list of events/regattas.
If competitor information is available, there is a filter box in the upper right corner of the races page.
The filter is searching for competitors by sail ID as well as the competitor's name.
</li>
<li>Regattas can now be filtered by boat category in the smartphone page.
The filter will be shown for events with multiple regattas if there is more than on boat category associated with the event.
</li>
</ul>
<h5 class="articleSubheadline">October 2015</h5>
<ul class="bulletList">
<li>A variety of tablets will now show the desktop page by default.
The mobile version of the website is primarily designed to be used on smartphones,
but you can manually switch back to the mobile version by clicking the corresponding link in the page footer.
</li>
<li>There is some amount of caching on the web page to reduce the consumed traffic for mobile users.
Contents aren't loaded again on each page switch. This improves the traffic consumption and loading speed on fast page switches.
</li>
<li>The fullscreen image viewer (start page and event media page) can automatically slide through the images.
This can be turned on by clicking the corresponding icon in the upper right corner of the fullscreen viewer.
</li>
<li>There is an optimized search and results page for mobile devices now.
</li>
</ul>
<h5 class="articleSubheadline">September 2015</h5>
<ul class="bulletList">
<li>Races in the event/regatta race list can now be shown in 'competition format'.
In this format the races and flights are shown in a structural view.
In contrast to the default list view there is no grouping by live and finished states and all
(including upcoming) races/flights are shown in one structural view.
</li>
<li>The mobile page now provides regatta overview pages for events having multiple regattas.
This page shows live races, an excerpt of the current results as well as statistics for the regatta.
</li>
</ul>
<h5 class="articleSubheadline">August 2015</h5>
<ul class="bulletList">
<li>Series events appear as only one entry in the events list, with a marker that
this is only one of a certain number of events in a series. This way, the many series events
don't produce clutter in the events list.
</li>
<li>Bug fix: memory leaks for distance cache have been solved. The issue could
also lead to endless recursions, ultimately leading to server lock-ups.
</li>
<li>Bug fix: master data import detached race logs and regatta logs from database.
Log entries added after import on the importing server were not written to the
database and therefore were lost upon server restart.
</li>
<li>There is a smartphone-optimized version of the "solutions" page known from the desktop.
</li>
<li>The smartphone event history now has a slider that allows swiping through the events of a year.
</li>
</ul>
<h5 class="articleSubheadline">July 2015</h5>
<ul class="bulletList">
<li>Major improvements to the web page, with specific enhancements for smartphone
users. Race lists are now sortable by various criteria by clicking on the column
headers. A new fancy progress indicator now also provides visual feedback for
multi-fleet regattas. A news ticker box has been implemented, and the event
overview page can hold a video player on its stage. The event media gallery section
has been greatly improved.
</li>
<li>Fixed a bug in the wind streamlet visualization. Now the wind streamlets
correctly show the wind at the location selected also in wind-up mode.
</li>
<li>Added the "Course Geometry" option to the map display. With this option selected,
the map will show an info text for each leg and the start and finish line (only
for one of start/finish line if they are identical), telling the length of the leg
or line, respectively, and its angle to the wind. For the start and finish line
the advantage of the favored side is shown in meters.
</li>
<li>The SAP Sailing Analytics start page is now available in the smartphone version.
This page features some events that are currently interesting as well as showing a
quickfinder for a variety of recent and upcoming events.
Previously, the start page redirected to the event history.
</li>
</ul>
<h5 class="articleSubheadline">May 2015</h5>
<ul class="bulletList">
<li>Competitors can have a separate flag image, such as a club flag to be displayed instead of
the nationality flag usually displayed with the sail number. This enables national league events
to display the club flags consistently when all nation flags would be the same anyway.
</li>
<li>The wind estimation from the tracked courses over ground has been improved. It now takes into
account any polar data available for the boat class concerned. In particular, this helps to rule
out very unlikely gybing angles in downwind clusters that otherwise could have led to an incorrect
split of the downwind fleet into two clusters where actually all boats were on the same tack.
</li>
<li>Some significant improvements have been applied to the simulator feature which is embedded in the
race viewer since some time now. The simulator now copes with very shifty wind conditions and finds
solutions in scenarios where an upwind leg flips to a reach or even into a downwind leg over time.
</li>
</ul>
<h5 class="articleSubheadline">April 2015</h5>
<ul class="bulletList">
<li>There is a new map setting "Wind-Up (show wind from top of map)". Selecting it will
align the map's top with the combined true wind average direction around the time currently
selected in the time slider. In this new view there are limitations as to how far you can zoom
out. Furthermore, you will not be able to use the Satellite view with this setting. Tip:
if you want to re-align to wind-up after a major wind shift, un-check the setting, confirm
the setting change, then set it again. A compass shows the true north orientation. Clicking
the compass will toggle between wind-up and north-up display.
</li>
<li>Bug fixes around addition of the "Regatta Rank" column in the leaderboard which could have
distorted the leaderboard's column layout under certain conditions.
</li>
<li>Bug fix for cumulative points in the leaderboard ("Show added scores for each entry in leaderboard").
With this setting, scores of competitors not tracked in the current race were not correctly represented
and may have stayed blank.
</li>
<li>Bug fix for the leg counter in the regatta page: when the first boat has finished, the leg counter went
to zero (e.g., "0 of 8"). This has now been fixed, and it will show, e.g., "8 of 8" upon the first boat
crossing the finish line.
</li>
</ul>
<h5 class="articleSubheadline">March 2015</h5>
<ul class="bulletList">
<li>Added competitor chart type for Course Over Ground (COG)
</li>
<li>Added leaderboard column and competitor chart entry for distances considering
gate starts. With gate starts, late starters sail a shorter distance in their first
leg. To still make distances comparable, a new metric has been introduced that for a gate
start adds the distance between the port side of the start line and the position where
the competitor started to the distance sailed in the first leg.
</li>
<li>The weights of the wind measurement units, the wind estimation and manual wind inputs by the
race committee are now used properly for the wind streamlet visualization. This fixes issues
with exaggerated weights for an estimated wind direction of very low confidence.
</li>
<li>The expand/collapse toggle button in the race viewer's time panel has correct arrows now.
</li>
</ul>
<h5 class="articleSubheadline">February 2015</h5>
<ul class="bulletList">
<li>The leaderboard offers a new column: "Start delay." It shows the number of seconds that passed
since the race start and the time the competitor passes the start mark / start line. As usual,
you can sort for this column. For line starts, this is a fairly direct measure of start quality
except for port starts which may have to be considered differently. For gate starts, you can use
this measure to find out about early and late starters and dig into possible correlations between
results and start time point.
</li>
</ul>
<h5 class="articleSubheadline">January 2015</h5>
<ul class="bulletList">
<li>The race viewer ("RaceBoard.html") now offers a new option in the map settings:
"Show simulation overlay". Checking this feature will simulate the current leader's leg
using different strategies and using the wind as it has been measured and estimated.
This feature becomes particularly interesting when combined with the "Show wind streamlets overlay"
feature that visualizes the spatial distribution of the wind field over the course.
</li>
<li>The race viewer is now more resilient against exceptional conditions such as the missing
of all wind information and the missing of a course with its waypoints. If no wind information
is available at all (no wind direction indicator is then shown in the upper left corner of
the map) then all ranking and distances will be based on a projection onto the course instead
of onto the wind direction, even for upwind and downwind legs.
</li>
</ul>
<h5 class="articleSubheadline">September 2014</h5>
<ul class="bulletList">
<li>The search feature now also includes results from currently ongoing events that may not yet be
included in our archive. Search keywords are now properly used in constructing the query.
</li>
</ul>
<h5 class="articleSubheadline">August 2014</h5>
<ul class="bulletList">
<li>The 2D race viewer, also known as the "Race Board," now comes with a fresh, new design. The orange
checkbox toggle buttons that open or close views such as the leaderboard or the charts for wind
and competitor comparison, have been replaced by tabs with "&gt;&gt;" symbols. Clicking on those
will expand or collapse the respective view. The views can now be resized using the handles attached
to the dividers separating the views. They work much better on mobile and touch devices than the splitters
used previously did. The settings buttons that used to be integrated with the orange toggle
checkbox buttons have now been moved into the views that they control. For example, the map settings
button is now displayed on the map, below the zoom control on the right map border.
</li>
<li>The leaderboard now has an improved search and filter control. Above the table displaying the
competitors and their figures is a new field with a text input that you can use to filter for
competitors, by their sail number or their name. While filtered, use the checkboxes in front of
the table row to add the competitor to or remove it from the selection. Use the "X" button to the
right of the search input field to clear the search. To the left of the search input field is a
filter symbol that you can use to select and create advanced filtering rules, such as filtering
by rank or nationality. Left of the filter button is the leaderboard settings button.
</li>
<li>The leaderboard has a new first column: a selection check box. We hope this makes it easier in
particular for users on mobile or other touch devices to select more than one competitor at a time.
The old-style multi-selection using the Control and Shift keys, as known well from typical desktop
file explorers, still work like they did before. Note that it is possible to sort for the checkbox
column, putting the selected competitors at the top of the leaderboard.
</li>
<li>When using the auto-zoom feature from the map settings, boats and wind streamlets will now
continue to move smoothly as long as the map only needs panning. Please also note that auto-zooming
to "tails" may slow down the display considerable, particularly when tracking at high frequency.
</li>
<li>When viewing a single race, the leaderboard now by default does no longer show the regatta rank as
the first column (and not as the second one either, given that the first column now usually shows
the checkboxes used for selecting items). If you'd like to get the regatta rank column back, also when
looking at a single race, please choose it from the leaderboard settings dialog where you can find it
in the "Overall Details" section. It may still be a nice column to have when following the last race
in a regatta. If you're just interested in the relative regatta rank ordering you may as well sort by
the right-most total points column which (surprising to some but welcome by most) sorts by the regatta
rank and not by the plain points sum (taking into account medal race and gold/silver/... fleet participations).
</li>
</ul>
<h5 class="articleSubheadline">July 2014</h5>
<ul class="bulletList">
<li>The advantage line now uses the wind at the position in the middle of the leg that the
competitor to which the line is attached is currently sailing in. Before, the "global" wind
average was used which is still displayed in the top left corner of the map. You can understand
the details by turning on the "Streamlet" wind visualization in the Map Settings.
</li>
<li>Faster wind streamlet loading: when you activate the streamlets for wind visualization, they now
display much faster.
</li>
<li>Faster boat position display: We found that what you're probably the most interested in when
you first open a race map or move the time slider to some very different point in time is the
position of the boats at that point in time; and you want to know it fast.
The tails which show how the boats got there are also interesting, but you won't blame us as much
if it takes a little bit to update them. So we've separated the placement of the boats from the
drawing of their tails. Now you may blame us for the tails temporarily not being attached to the
boats anymore ;-).
</li>
</ul>
<h5 class="articleSubheadline">June 2014</h5>
<ul class="bulletList">
<li>In the "Maps" settings in the RaceBoard page there is a new checkbox named
"Show wind streamlets overlay." If you select it and wait for about a minute,
a complete wind field will be calculated and visualized as an overlay on top
of the race course display. It reacts to you moving the time slider and shows
how the wind differs at different locations on the course. Stronger and faster-moving
streamlets mean stronger breeze.
</li>
<li>Wind is now calculated location-based. At locations closer to a sensor, that sensor
has a higher influence on the local wind field than sensors further away. The wind
estimations based on the courses sailed are assigned a location that corresponds with
the "center of gravity" of the field that was used for the estimation. For example,
if the fleet is approaching the windward mark and the wind sensor in on the committee
boat on the leeward side of the course, the course-based estimation influences the
assumed wind field more on the windward side whereas the sensor counts more for
the leeward side.
</li>
<li>Switched to the latest version of the Highcharts charting framework. This leads to improvements
especially on mobile devices. Charts can be zoomed using pinch gestures now.
</li>
<li>To support the use of the SAP Sailing Analytics also for live video streaming formats, we
introduced a new URL parameter <tt>viewShowMapControls</tt> for the <tt>Spectator.html</tt>
and the <tt>RaceBoard.html</tt> pages. Setting this parameter to "false"
will suppress the zoom, pan and map type selection
controls on the map display. Example:
<a href="http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false">http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false</a>.
When used for the <tt>Spectator.html</tt> URL, this will apply
to all <tt>RaceBoard.html</tt> pages (all races) reached from there. Of course, the map
can still be panned and zoomed using the usual mouse gestures. A left double click zooms
in, a right double click zooms out. Zoom can also be controlled using the mouse wheel.
And panning works by clicking on the map and dragging the mouse. Without the map type
control, switching to satellite view is not possible (keep this in mind when tracking
races in China, see below for Google's random coordinate transformations there).
</li>
</ul>
<h5 class="articleSubheadline">May 2014</h5>
<ul class="bulletList">
<li>Fixed an issue with the wind speed charts. Charts that only had a single, unchanged
wind speed crashed the browser. The solution scales the right hand side Y-axis denoting
the wind speed such that it always starts at 0kts.
</li>
<li>Analysis of tacks and jibes around mark passings has now been improved. Before,
a jibe followed by a tack that turns in the same direction (e.g., to port) so far
was always called a penalty maneuver. Now, if a mark passing separates such a tack
from such a jibe, no penalty is called anymore, and the two maneuvers are identified
correctly.
</li>
<li>Another note on maneuver analysis: we received questions regarding the maneuvers
involved in clearing an OCS. The SAP Sailing Analytics treat a clearing of
an OCS, going back behind the start line and crossing it again, as if the competitor
started again. In particular this means that all maneuvers prior to this new start
do not count for the metrics; neither does the distance sailed between the first (OCS)
start and the clear start.
</li>
<li>A hint regarding the "distance to start line" metrics: please keep in mind that currently this
measurement is calculated as the distance of the GPS tracker from the line. Unless the tracker
is mounted near the bow (or at the bow sprit, if any), the distance calculated this way will
be greater than what counts for crossing the line. Ordering, comparing and aggregating over this
figure still makes sense as long as you remember this detail.
</li>
<li>Introduced a new leaderboard entry "XTE +/-" featuring a signed cross-track error which shows
for a leg and also aggregated for an entire race on which side of the leg a competitor stayed on
average. Currently, the "side" is defined as viewing in the leg's direction, so "left" for a downwind
leg is the same side as "right" for the corresponding upwind leg.
</li>
</ul>
<h5 class="articleSubheadline">April 2014</h5>
<ul class="bulletList">
<li>China races, such as in Qingdao, may have the map background displayed at slightly wrong locations.
This is due to an intentional random transformation that Google applies to GPS locations for
China. You can circumvent this issue by switching to satellite view where the map background is displayed
correctly. <img src="/images/mapsatellite.png"/>
</li>
<li>Fixed a bug that in some cases has caused the wind chart, the advantage line, the wind indicator on the map
and the leaderboard to use slightly different wind values. The problem was related to different averaging and
damping methods used for these values which has now been aligned.
</li>
<li>Fixed a bug that caused the time spent in a leg, race or regatta to always show the time at the end of the leg
if the leg has been started by the competitor at the time point queried, e.g., by the time slider. While this
did not have any serious effects in live mode, for replay races this left the leg time and race time constant
at the total time spent so far up to the end of the last leg the competitor had started at that time.
</li>
</ul>
<h5 class="articleSubheadline">March 2014</h5>
<ul class="bulletList">
<li>Added a new setting for a leaderboard called "Show added scores". Using this feature one
can display added scores for each leaderboard entry. An added score is the sum over all scores
from the first column to the current column. One can describe it as an intermediate overall score.
This feature can be used to know the score until a specific race while another race is live and
constantly changes the overall score.</li>
<li>Added tooltips for leaderboard settings.</li>
<li>Fixed HighPointOverall scheme to also break a tie by looking at the ranks of the last regatta.</li>
<li>Progress indicator when expanding a race in the RaceBoard view</li>
<li>Leaderboard column "GPS Lag" shows how far a competitor's GPS signal lags behind the average
tracking quality. A background color bar classifies into good/ok/bad (green/yellow/red).</li>
</ul>
<h5 class="articleSubheadline">February 2014</h5>
<ul class="bulletList">
<li>Fixed an issue with boat rotation on Safari browsers</li>
<li>Maneuver analysis now counts tacks and jibes based on wind
at boat position and not based on leg type; subsequently,
penalty circle analysis is now based on a tack and a jibe
in close succession instead of a full circle.</li>
<li>Regatta rank and overall rank in competitor chart of leaderboard panel
has been fixed; it did erroneously display the rank of the particular
race, not the regatta/overall rank after that race.</li>
<li>Regatta overview now also displays all regatta leaderboards</li>
<li>Boat colors match tail colors</li>
<li>When selecting boats in leaderboard, de-selected boats' tails turn grey</li>
</ul>
<h5 class="articleSubheadline">January 2014</h5>
<ul class="bulletList">
<li>Boat movement animated smoothly</li>
<li>Start and Finish line show line parameters (length, angle to wind,
advantage) when mouse hovers over them</li>
<li>Start line has a count-down indicator displayed next to it
before the start. The count-down is also displayed underneath the time
slider</li>
<li>Competitor and Wind charts show pre-start data where possible</li>
<li>Competitors can be assigned a fixed color which they will keep
for all races in a regatta</li>
<li>By default, videos are now shown in a moveable, resizable box embedded in
the web page. For a two screen setup, the embedded box can be popped out to a
dedicated browser window.</li>
<li>Official score corrections for historic races are only applied after
the respective competitor finished the race. Therefore, in-between race
scores now correspond with tracking, regardless of final corrected score.</li>
<li>By using negative playback speed (e.g., -1), replays can be run backwards.</li>
</ul>
</div>
</div>
</body>
</html>
@@ -4,6 +4,8 @@ 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.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
@@ -14,6 +16,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.home.desktop.places.whatsnew.WhatsNewPlace.WhatsNewNavigationTabs;
import com.sap.sailing.gwt.home.mobile.app.MobilePlacesNavigator;
import com.sap.sailing.gwt.home.shared.utils.CollapseAnimation;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class Solutions extends Composite {
@@ -22,6 +25,7 @@ public class Solutions extends Composite {
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
@UiField StringMessages i18n;
@UiField DivElement sapInSailingDiv;
@UiField DivElement sapSailingAnalyticsUi;
@UiField DivElement raceCommitteeAppUi;
@@ -35,10 +39,29 @@ public class Solutions extends Composite {
@UiField AnchorElement sailInSightAppDetailsAnchor;
@UiField AnchorElement buoyPingerAppDetailsAnchor;
@UiField AnchorElement simulatorAppDetailsAnchor;
@UiField AnchorElement raceManagerPlayStoreLinkUi;
@UiField AnchorElement inSightAppStoreLinkUi;
@UiField AnchorElement buoyPingerPlayStoreLinkUi;
@UiField AnchorElement inSightPlayStoreLinkUi;
@UiField ImageElement raceManagerPlayStoreImgUi;
@UiField ImageElement inSightPlayStoreImgUi;
@UiField ImageElement buoyPingerPlayStoreImgUi;
@UiField ImageElement inSightAppStoreImgUi;
public Solutions(MobilePlacesNavigator placesNavigator) {
SolutionsResources.INSTANCE.css().ensureInjected();
initWidget(uiBinder.createAndBindUi(this));
String playstorebadgeSrc = UriUtils.fromString(i18n.playstoreBadge()).asString();
String insightAppstoreSrc = UriUtils.fromString("images/home/appstore" + i18n.appstoreBadgeSuffix() + ".svg")
.asString();
raceManagerPlayStoreLinkUi.setHref(UriUtils.fromString(i18n.playstoreRacecommitteeApp()));
inSightAppStoreLinkUi.setHref(UriUtils.fromString(i18n.appstoreSapSailInsight()));
buoyPingerPlayStoreLinkUi.setHref(UriUtils.fromString(i18n.playStoreBuoyPingerApp()));
inSightPlayStoreLinkUi.setHref(UriUtils.fromString(i18n.playstoreInsightApp()));
raceManagerPlayStoreImgUi.setSrc(playstorebadgeSrc);
inSightPlayStoreImgUi.setSrc(playstorebadgeSrc);
buoyPingerPlayStoreImgUi.setSrc(playstorebadgeSrc);
inSightAppStoreImgUi.setSrc(insightAppstoreSrc);
initWhatsNewLink(placesNavigator, WhatsNewNavigationTabs.SailingAnalytics, sailingAnalyticsDetailsAnchor);
initWhatsNewLink(placesNavigator, WhatsNewNavigationTabs.RaceManagerApp, raceManagerAppDetailsAnchor);
@@ -110,13 +110,10 @@
class="{style.storeBadgesContainer}"
>
<a ui:field="raceManagerPlayStoreLinkUi"
href="{i18n.playstoreRacecommitteeApp}"
target="_blank"
><img
ui:field="raceManagerPlayStoreImgUi"
alt="Get it on Google Play"
src="{i18n.playstoreBadge}"
/></a>
alt="Get it on Google Play" /></a>
</div>
<a class="{res.mainCss.button}" ui:field="raceManagerAppDetailsAnchor">
<ui:text from="{i18n.sapSailingRaceManagerReadMore}"/>
@@ -139,25 +136,20 @@
</p>
<div class="{style.storeBadgesContainer}">
<a ui:field="inSightAppStoreLinkUi"
href="{i18n.appstoreSapSailInsight}"
target="_blank"
><img
ui:field="inSightAppStoreImgUi"
alt="Get it on AppStore"
src="images/home/appstore{i18n.appstoreBadgeSuffix}.svg"
/></a>
</div>
<div
class="{style.storeBadgesContainer}"
>
<a ui:field="inSightPlayStoreLinkUi"
href="{i18n.playstoreInsightApp}"
target="_blank"
><img
ui:field="inSightPlayStoreImgUi"
alt="Get it on Google Play"
src="{i18n.playstoreBadge}"
/></a>
alt="Get it on Google Play"/></a>
</div>
<a class="{res.mainCss.button}" ui:field="sailInSightAppDetailsAnchor">
<ui:text from="{i18n.sapSailInSightReadMore}"/>
@@ -182,13 +174,10 @@
class="{style.storeBadgesContainer}"
>
<a ui:field="buoyPingerPlayStoreLinkUi"
href="{i18n.playStoreBuoyPingerApp}"
target="_blank"
><img
ui:field="buoyPingerPlayStoreImgUi"
alt="Get it on Google Play"
src="{i18n.playstoreBadge}"
/></a>
alt="Get it on Google Play"/></a>
</div>
<a class="{res.mainCss.button}" ui:field="buoyPingerAppDetailsAnchor">
<ui:text from="{i18n.sapSailingBuoyPingerReadMore}"/>
@@ -4,6 +4,8 @@ import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.http.client.UrlBuilder;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceHistoryMapper;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
@@ -41,6 +43,10 @@ public class PlaceNavigation<T extends Place> {
return buildPlaceUrl();
}
public SafeUri getSafeTargetUrl() {
return UriUtils.fromString(getTargetUrl());
}
public String getHistoryUrl() {
String placeUrl = buildPlaceUrl();
if (placeUrl.startsWith("#")) {
@@ -20,6 +20,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
@@ -94,7 +96,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a target=\"_blank\" href=\"{0}\">{1}</a>")
SafeHtml cell(String url, String displayName);
SafeHtml cell(SafeUri safeUri, String displayName);
}
private static AnchorTemplates ANCHORTEMPLATE = GWT.create(AnchorTemplates.class);
@@ -228,7 +230,7 @@ public class EventListComposite extends Composite implements EventsRefresher, Le
if(event != null && event.id != null){
link = EntryPointLinkFactory.createEventPlaceLink(event.id.toString(), new HashMap<String, String>());
}
return ANCHORTEMPLATE.cell(link, event.getName());
return ANCHORTEMPLATE.cell(UriUtils.fromString(link), event.getName());
}
};
@@ -12,6 +12,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
@@ -60,7 +62,7 @@ public class ImagesListComposite extends Composite {
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a target=\"_blank\" href=\"{0}\">{1}</a>")
SafeHtml cell(String url, String displayName);
SafeHtml cell(SafeUri url, String displayName);
}
private static AnchorTemplates ANCHORTEMPLATE = GWT.create(AnchorTemplates.class);
@@ -155,7 +157,7 @@ public class ImagesListComposite extends Composite {
if(index > 0) {
linkName = link.substring(index+1, link.length());
}
return ANCHORTEMPLATE.cell(image.getSourceRef(), linkName);
return ANCHORTEMPLATE.cell(UriUtils.fromString(image.getSourceRef()), linkName);
}
};
@@ -18,6 +18,7 @@ import com.google.gwt.event.dom.client.ClickHandler;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
@@ -88,7 +89,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a target=\"_blank\" href=\"{0}\">{1}</a>")
SafeHtml cell(String url, String displayName);
SafeHtml cell(SafeUri url, String displayName);
}
public LeaderboardConfigPanel(final SailingServiceAsync sailingService, RegattaRefresher regattaRefresher,
@@ -150,7 +151,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
final String link = EntryPointWithSettingsLinkFactory.createLeaderboardLink(
new LeaderboardContextDefinition(object.name, object.displayName),
new LeaderboardPerspectiveOwnSettings(showRaceDetails));
return ANCHORTEMPLATE.cell(link, object.name);
return ANCHORTEMPLATE.cell(UriUtils.fromString(link), object.name);
}
};
@@ -15,6 +15,8 @@ import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.user.cellview.client.CellTable.Resources;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
@@ -62,7 +64,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a href=\"{0}\">{1}</a>")
SafeHtml cell(String url, String displayName);
SafeHtml cell(SafeUri url, String displayName);
}
private static AdminConsoleTableResources tableResources = GWT.create(AdminConsoleTableResources.class);
@@ -508,7 +510,7 @@ public class LeaderboardGroupConfigPanel extends AbstractRegattaPanel implements
String link = URLEncoder.encode("/gwt/Spectator.html?leaderboardGroupName=" + group.getName()
+ "&showRaceDetails=true&"+RaceBoardPerspectiveOwnSettings.PARAM_CAN_REPLAY_DURING_LIVE_RACES+"=true"
+ (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : ""));
return ANCHORTEMPLATE.cell(link, group.getName());
return ANCHORTEMPLATE.cell(UriUtils.fromString(link), group.getName());
}
};
groupNameColumn.setSortable(true);
@@ -6,6 +6,7 @@ import java.util.Comparator;
import com.google.gwt.core.client.GWT;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.safehtml.shared.UriUtils;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
@@ -63,7 +64,8 @@ extends TableWrapper<RaceColumnDTOAndFleetDTOWithNameBasedEquality, S> {
String link = EntryPointWithSettingsLinkFactory.createRaceBoardLink(raceboardContext,
settings);
return ANCHORTEMPLATE.cell(link, raceInLeaderboardDTOAndFleetName.getA().getRaceColumnName());
return ANCHORTEMPLATE.cell(UriUtils.fromString(link),
raceInLeaderboardDTOAndFleetName.getA().getRaceColumnName());
} else {
return SafeHtmlUtils.fromString(raceInLeaderboardDTOAndFleetName.getA().getRaceColumnName());
}
@@ -12,6 +12,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
@@ -58,7 +60,7 @@ public class VideosListComposite extends Composite {
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a target=\"_blank\" href=\"{0}\">{1}</a>")
SafeHtml cell(String url, String displayName);
SafeHtml cell(SafeUri url, String displayName);
}
private static AnchorTemplates ANCHORTEMPLATE = GWT.create(AnchorTemplates.class);
@@ -138,7 +140,7 @@ public class VideosListComposite extends Composite {
if(linkName.length() >= 25) {
linkName = linkName.substring(0, 22) + "...";
}
return ANCHORTEMPLATE.cell(video.getSourceRef(), linkName);
return ANCHORTEMPLATE.cell(UriUtils.fromString(video.getSourceRef()), linkName);
}
};
@@ -1342,6 +1342,7 @@ raceIsPlanned=Planned
raceIsCanceled=Abandoned
raceIsPostponed=Postponed
startingInMinutes=Starts in {0,number} min
startingInMinutes[one]=Starts in {0,number} min
startingInMinutes[none]=Starting now
latestNews=Latest News
resultsUpdate=Result update
@@ -1325,6 +1325,7 @@ raceIsPlanned=Geplant
raceIsCanceled=Abgebrochen
raceIsPostponed=Verschoben
startingInMinutes=Startet in {0,number} min
startingInMinutes[one]=Startet in {0,number} min
startingInMinutes[none]=Startet gleich
latestNews=Neuigkeiten
resultsUpdate=Neue Ergebnisse
@@ -5,15 +5,17 @@ import static com.google.gwt.dom.client.BrowserEvents.MOUSEDOWN;
import java.util.List;
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.Element;
import com.google.gwt.dom.client.EventTarget;
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.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.safehtml.shared.UriUtils;
/**
* Represent cell with multiple links. Link is represented as static nested class {@link CellLink}.
@@ -76,7 +78,7 @@ public class MultipleLinkCell extends AbstractCell<List<MultipleLinkCell.CellLin
*/
interface LinkTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a target=\"{0}\" href=\"{1}\">{2}</a>")
SafeHtml cell(String target, String href, String text);
SafeHtml cell(String target, SafeUri href, String text);
}
private static LinkTemplates linkTemplates = GWT.create(LinkTemplates.class);
@@ -137,7 +139,7 @@ public class MultipleLinkCell extends AbstractCell<List<MultipleLinkCell.CellLin
} else {
sb.appendHtmlConstant("<br>");
}
rendered = linkTemplates.cell(link.getTarget(), link.getHref(), link.getText());
rendered = linkTemplates.cell(link.getTarget(), UriUtils.fromString(link.getHref()), link.getText());
sb.append(rendered);
}
}
@@ -40,10 +40,13 @@ public interface DataMiningService extends RemoteService {
HashSet<FunctionDTO> dimensionDTOs, HashMap<DataRetrieverLevelDTO, SerializableSettings> retrieverSettings,
HashMap<DataRetrieverLevelDTO, HashMap<FunctionDTO, HashSet<? extends Serializable>>> filterSelectionDTO, String localeInfoName);
<ResultType> QueryResultDTO<ResultType> runQuery(DataMiningSession session, StatisticQueryDefinitionDTO queryDefinition);
<ResultType extends Serializable> QueryResultDTO<ResultType> runQuery(DataMiningSession session,
StatisticQueryDefinitionDTO queryDefinition);
HashSet<PredefinedQueryIdentifier> getPredefinedQueryIdentifiers();
<ResultType> QueryResultDTO<ResultType> runPredefinedQuery(DataMiningSession session, PredefinedQueryIdentifier identifier, String localeInfoName);
<ResultType extends Serializable> QueryResultDTO<ResultType> runPredefinedQuery(DataMiningSession session,
PredefinedQueryIdentifier identifier, String localeInfoName);
SerializationDummy pseudoMethodSoThatSomeClassesAreAddedToTheGWTSerializationPolicy();
@@ -45,10 +45,14 @@ public interface DataMiningServiceAsync {
HashMap<DataRetrieverLevelDTO, HashMap<FunctionDTO, HashSet<? extends Serializable>>> filterSelectionDTO,
String localeInfoName, AsyncCallback<QueryResultDTO<HashSet<Object>>> callback);
<ResultType> void runQuery(DataMiningSession session, StatisticQueryDefinitionDTO queryDefinition, AsyncCallback<QueryResultDTO<ResultType>> callback);
<ResultType extends Serializable> void runQuery(DataMiningSession session,
StatisticQueryDefinitionDTO queryDefinition, AsyncCallback<QueryResultDTO<ResultType>> callback);
void getPredefinedQueryIdentifiers(AsyncCallback<HashSet<PredefinedQueryIdentifier>> callback);
<ResultType> void runPredefinedQuery(DataMiningSession session, PredefinedQueryIdentifier identifier, String localeInfoName, AsyncCallback<QueryResultDTO<ResultType>> callback);
<ResultType extends Serializable> void runPredefinedQuery(DataMiningSession session,
PredefinedQueryIdentifier identifier, String localeInfoName,
AsyncCallback<QueryResultDTO<ResultType>> callback);
/**
* This method does nothing, but is needed to ensure, that some classes for the data mining
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.datamining.developer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -140,14 +141,15 @@ public class PredefinedQueryRunner extends ComponentWithoutSettings {
protected void runSelectedPredefinedQuery() {
PredefinedQueryIdentifier predefinedQueryIdentifier = selectionListBox.getValue();
resultsPresenter.showBusyIndicator();
dataMiningService.runPredefinedQuery(session, predefinedQueryIdentifier, LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback<QueryResultDTO<Object>>() {
dataMiningService.runPredefinedQuery(session, predefinedQueryIdentifier,
LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback<QueryResultDTO<Serializable>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError("Error running the query: " + caught.getMessage());
resultsPresenter.showError(stringMessages.errorRunningDataMiningQuery() + ".");
}
@Override
public void onSuccess(QueryResultDTO<Object> result) {
public void onSuccess(QueryResultDTO<Serializable> result) {
resultsPresenter.showResult(result);
}
});
@@ -1,11 +1,14 @@
package com.sap.sailing.gwt.ui.datamining.execution;
import java.io.Serializable;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.gwt.ui.datamining.ManagedDataMiningQueriesCounter;
import com.sap.sse.datamining.shared.data.QueryResultState;
import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO;
public abstract class ManagedDataMiningQueryCallback<AggregatedType> implements AsyncCallback<QueryResultDTO<AggregatedType>> {
public abstract class ManagedDataMiningQueryCallback<AggregatedType extends Serializable>
implements AsyncCallback<QueryResultDTO<AggregatedType>> {
private final ManagedDataMiningQueriesCounter counter;
@@ -1,5 +1,7 @@
package com.sap.sailing.gwt.ui.datamining.execution;
import java.io.Serializable;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
@@ -93,14 +95,15 @@ public class SimpleQueryRunner extends AbstractComponent<QueryRunnerSettings> im
if (errorMessages == null || !errorMessages.iterator().hasNext()) {
counter.increase();
resultsPresenter.showBusyIndicator();
dataMiningService.runQuery(session, queryDefinition, new ManagedDataMiningQueryCallback<Object>(counter) {
dataMiningService.runQuery(session, queryDefinition,
new ManagedDataMiningQueryCallback<Serializable>(counter) {
@Override
protected void handleFailure(Throwable caught) {
errorReporter.reportError("Error running the query: " + caught.getMessage());
resultsPresenter.showError(stringMessages.errorRunningDataMiningQuery() + ".");
}
@Override
protected void handleSuccess(QueryResultDTO<Object> result) {
protected void handleSuccess(QueryResultDTO<Serializable> result) {
resultsPresenter.showResult(result);
}
});
@@ -30,6 +30,8 @@ import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesBuilder;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
@@ -78,9 +80,9 @@ import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.impl.InvertibleComparatorAdapter;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardPanelLifecycle;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettingsDialogComponent;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettingsFactory;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
import com.sap.sailing.gwt.ui.client.Collator;
import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener;
@@ -155,20 +157,44 @@ public class LeaderboardPanel extends AbstractCompositeComponent<LeaderboardSett
private static final String STYLE_LEADERBOARD_INFO = "leaderboardInfo";
private static final String STYLE_LEADERBOARD_TOOLBAR = "leaderboardContent-toolbar";
private static final String STYLE_LEADERBOARD_LIVE_RACE = "leaderboardContent-liverace";
private static RaceColumnTemplate raceColumnTemplate = new RaceColumnTemplate();
interface RaceColumnTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<div style=\"color:{0}; border-bottom: 3px solid {1}\">")
SafeHtml cellFrameWithTextColorAndFleetBorder(String textColor, String borderStyle);
static class RaceColumnTemplate {
private MyTemplate template = GWT.create(MyTemplate.class);
@SafeHtmlTemplates.Template("<div style=\"color:{0};\">")
SafeHtml cellFrameWithTextColor(String textColor);
interface MyTemplate extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<div style='{0}'>")
SafeHtml styledDiv(SafeStyles style);
}
@SafeHtmlTemplates.Template("<div style=\"color:{0};\">")
SafeHtml cellWithImageResourceAndText(ImageResource image, String text);
/**
* Originally in the safehtml template: color:{0}; border-bottom: 3px solid {1};
*
* @param textColor
* @param borderColor
* @return
*/
public SafeHtml cellFrameWithTextColorAndFleetBorder(String textColor, String borderColor) {
SafeStylesBuilder sb = new SafeStylesBuilder();
sb.trustedColor(textColor);
sb.trustedNameAndValue("border-bottom", "3px solid " + borderColor);
return template.styledDiv(sb.toSafeStyles());
}
/**
*
* Originally in the safehtml template: color:{0};
*
* @param textColor
* @return
*/
public SafeHtml cellFrameWithTextColor(String textColor) {
SafeStylesBuilder sb = new SafeStylesBuilder();
sb.trustedColor(textColor);
return template.styledDiv(sb.toSafeStyles());
}
}
private static RaceColumnTemplates raceColumnTemplate = GWT.create(RaceColumnTemplates.class);
/**
* The leaderboard name is used to
* {@link SailingServiceAsync#getLeaderboardByName(String, java.util.Date, String[], boolean, String, com.google.gwt.user.client.rpc.AsyncCallback)
@@ -4,6 +4,8 @@ import java.util.Comparator;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesBuilder;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
@@ -18,21 +20,33 @@ import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
*/
public class MinMaxRenderer {
protected interface Templates extends SafeHtmlTemplates {
@Template("<div title=\"{2}\" class=\"{1}\" style=\"background-size:{3}% 25px;\">{0}</div>")
SafeHtml render(String value, String cssClass, String title, int percentage);
}
protected static final String BACKGROUND_BAR_STYLE_BAD = "minMaxBackgroundBarBad";
protected static final String BACKGROUND_BAR_STYLE_OK = "minMaxBackgroundBar";
protected static final String BACKGROUND_BAR_STYLE_GOOD = "minMaxBackgroundBarGood";
protected static final Templates TEMPLATES = GWT.create(Templates.class);
private final HasStringAndDoubleValue valueProvider;
private final Comparator<LeaderboardRowDTO> comparator;
private Double minimumValue;
private Double maximumValue;
public static final Templates TEMPLATES = GWT.create(Templates.class);
public static class Templates {
interface MyTemplate extends SafeHtmlTemplates {
@Template("<div title=\"{2}\" class=\"{1}\" style='{3}'>{0}</div>")
SafeHtml render(String value, String cssClass, String title, SafeStyles style);
}
protected static final MyTemplate T = GWT.create(MyTemplate.class);
SafeHtml render(String value, String cssClass, String title, int percentage) {
SafeStylesBuilder sb = new SafeStylesBuilder();
sb.trustedNameAndValue("background-size", percentage + "% 25px");
return T.render(value, cssClass, title, sb.toSafeStyles());
}
}
/**
* Renders the value and the percentage bar of the columns {@link DetailTypeColumn} and
* {@link ManeuverCountRaceColumn}.
@@ -294,7 +294,8 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM
}
@Override
public <ResultType> QueryResultDTO<ResultType> runQuery(DataMiningSession session, StatisticQueryDefinitionDTO queryDefinitionDTO) {
public <ResultType extends Serializable> QueryResultDTO<ResultType> runQuery(DataMiningSession session,
StatisticQueryDefinitionDTO queryDefinitionDTO) {
SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, queryDefinitionDTO.getDataRetrieverChainDefinition().getName()));
DataMiningServer dataMiningServer = getDataMiningServer();
StatisticQueryDefinition<RacingEventService, ?, ?, ResultType> queryDefinition = dataMiningServer.getQueryDefinitionForDTO(queryDefinitionDTO);
@@ -314,7 +315,8 @@ public class DataMiningServiceImpl extends RemoteServiceServlet implements DataM
}
@Override
public <ResultType> QueryResultDTO<ResultType> runPredefinedQuery(DataMiningSession session, PredefinedQueryIdentifier identifier, String localeInfoName) {
public <ResultType extends Serializable> QueryResultDTO<ResultType> runPredefinedQuery(DataMiningSession session,
PredefinedQueryIdentifier identifier, String localeInfoName) {
SecurityUtils.getSubject().checkPermission(Permission.DATA_MINING.getStringPermissionForObjects(Mode.READ, identifier.getIdentifier()));
DataMiningServer dataMiningServer = getDataMiningServer();
ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = dataMiningServer.getPredefinedQueryDefinitionDTO(identifier);
@@ -1,5 +1,3 @@
@CHARSET "ISO-8859-1";
.cellTableWidget {
margin: 0;
table-layout: auto;
@@ -17,6 +17,8 @@ import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortEvent;
@@ -64,7 +66,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel {
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a href=\"{0}\">{1}</a>")
SafeHtml anchor(String url, String displayName);
SafeHtml anchor(SafeUri url, String displayName);
}
public static final String STYLE_NAME_PREFIX = "groupOverviewPanel-";
@@ -233,7 +235,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel {
public SafeHtml getValue(LeaderboardGroupDTO group) {
String link = new LinkWithSettingsGenerator<SpectatorSettings>(new SpectatorContextDefinition(group.getName()))
.createUrl(new SpectatorSettings(showRaceDetails));
return ANCHORTEMPLATE.anchor(link, group.getName());
return ANCHORTEMPLATE.anchor(UriUtils.fromString(link), group.getName());
}
};
groupsNameColumn.setSortable(true);
@@ -362,7 +364,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel {
+ (showRaceDetails ? "&showRaceDetails=true" : "")
+ "&leaderboardGroupName=" + selectedGroup.getName() + "&root=overview"
+ (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : ""));
return ANCHORTEMPLATE.anchor(link, leaderboard.name);
return ANCHORTEMPLATE.anchor(UriUtils.fromString(link), leaderboard.name);
}
};
@@ -456,7 +458,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel {
+ raceId.getRegattaName() + "&leaderboardGroupName=" + selectedGroup.getName()
+ "&root=overview"
+ (debugParam != null && !debugParam.isEmpty() ? "&gwt.codesvr=" + debugParam : ""));
name = ANCHORTEMPLATE.anchor(link, raceDisplayName);
name = ANCHORTEMPLATE.anchor(UriUtils.fromString(link), raceDisplayName);
} else {
name = new SafeHtmlBuilder().appendHtmlConstant(raceDisplayName).toSafeHtml();
}
@@ -9,9 +9,13 @@ import java.util.Map;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.safecss.shared.SafeStyles;
import com.google.gwt.safecss.shared.SafeStylesBuilder;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.rpc.AsyncCallback;
@@ -61,10 +65,10 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
interface AnchorTemplates extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<a class=\"{2}\" href=\"{0}\">{1}</a>")
SafeHtml anchor(String url, String displayName, String styleClass);
SafeHtml anchor(SafeUri url, String displayName, String styleClass);
@SafeHtmlTemplates.Template("<a target=\"{3}\" class=\"{2}\" href=\"{0}\">{1}</a>")
SafeHtml anchorWithTarget(String url, String displayName, String styleClass, String target);
SafeHtml anchorWithTarget(SafeUri url, String displayName, String styleClass, String target);
}
interface TextWithClassTemplate extends SafeHtmlTemplates {
@@ -76,8 +80,8 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
}
interface ColorBoxTemplate extends SafeHtmlTemplates {
@SafeHtmlTemplates.Template("<div class=\"{1}\" style=\"background:{0};\">&nbsp;</div>")
SafeHtml colorBox(String htmlColor, String styleClass);
@SafeHtmlTemplates.Template("<div class=\"{1}\" style=\"{0}\">&nbsp;</div>")
SafeHtml colorBox(SafeStyles htmlColor, String styleClass);
@SafeHtmlTemplates.Template("<div class=\"{0}\">&nbsp;</div>")
SafeHtml nocolorBox(String styleClass);
@@ -241,7 +245,8 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
new LeaderboardContextDefinition(leaderboard.name, leaderboard.displayName,
leaderboardGroup.getName()),
new LeaderboardPerspectiveOwnSettings(showRaceDetails, isEmbedded));
return getAnchor(link, stringMessages.leaderboard(), STYLE_ACTIVE_LEADERBOARD);
return getAnchor(link, stringMessages.leaderboard(),
STYLE_ACTIVE_LEADERBOARD);
}
};
@@ -308,7 +313,8 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
for (SeriesDTO series : regatta.series) {
// render the series name
if (!LeaderboardNameConstants.DEFAULT_SERIES_NAME.equals(series.getName())) {
seriesGrid.setHTML(seriesRow, 0, TEXTTEMPLATE.textWithClass(series.getName(), 50, STYLE_TABLE_TEXT));
seriesGrid.setHTML(seriesRow, 0, TEXTTEMPLATE.textWithClass(series.getName(), 50,
STYLE_TABLE_TEXT));
}
seriesGridFormatter.setVerticalAlignment(seriesRow, 0, HasVerticalAlignment.ALIGN_MIDDLE);
int numberOfFleets = series.getFleets().size();
@@ -320,10 +326,12 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
for(FleetDTO fleet: series.getFleets()) {
Color color = fleet.getColor();
if(color != null) {
fleetsGrid.setHTML(fleetRow, 0, COLORBOXTEMPLATE.colorBox(color.getAsHtml(), STYLE_COLORBOX));
SafeStyles bgStyle = new SafeStylesBuilder().trustedBackgroundColor(color.getAsHtml()).toSafeStyles();
fleetsGrid.setHTML(fleetRow, 0, COLORBOXTEMPLATE.colorBox(bgStyle, STYLE_COLORBOX));
fleetGridsFormatter.setVerticalAlignment(fleetRow, 0, HasVerticalAlignment.ALIGN_MIDDLE);
}
fleetsGrid.setHTML(fleetRow, 1, TEXTTEMPLATE.textWithClass(fleet.getName(), 50, STYLE_TABLE_TEXT));
fleetsGrid.setHTML(fleetRow, 1, TEXTTEMPLATE.textWithClass(fleet.getName(), 50,
STYLE_TABLE_TEXT));
fleetGridsFormatter.setVerticalAlignment(fleetRow, 1, HasVerticalAlignment.ALIGN_MIDDLE);
List<RaceColumnDTO> raceColumnsOfSeries = getRacesOfFleet(leaderboard, series, fleet);
fleetsGrid.setHTML(fleetRow, 2, renderRacesToHTml(leaderboard.name, raceColumnsOfSeries, fleet));
@@ -338,7 +346,8 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
if (!LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleet.getName())) {
Grid fleetsGrid = new Grid(1, 2);
CellFormatter fleetGridsFormatter = fleetsGrid.getCellFormatter();
fleetsGrid.setHTML(0, 0, TEXTTEMPLATE.textWithClass(displayName, 50, STYLE_TABLE_TEXT));
fleetsGrid.setHTML(0, 0, TEXTTEMPLATE.textWithClass(displayName, 50,
STYLE_TABLE_TEXT));
fleetGridsFormatter.setVerticalAlignment(0, 0, HasVerticalAlignment.ALIGN_MIDDLE);
fleetsGrid.setHTML(0, 1, renderRacesToHTml(leaderboard.name, raceColumnsOfSeries, fleet));
seriesGrid.setWidget(seriesRow, 1, fleetsGrid);
@@ -400,18 +409,20 @@ public class LeaderboardGroupPanel extends SimplePanel implements HasWelcomeWidg
} else if (race.trackedRace.hasGPSData && race.trackedRace.hasWindData) {
b.append(getAnchor(link, raceColumnName, STYLE_ACTIVE_RACE));
} else {
b.append(TEXTTEMPLATE.textWithClass(raceColumnName, STYLE_INACTIVE_RACE));
b.append(TEXTTEMPLATE.textWithClass(raceColumnName,
STYLE_INACTIVE_RACE));
}
} else {
b.append(TEXTTEMPLATE.textWithClass(raceColumnName, STYLE_INACTIVE_RACE));
b.append(
TEXTTEMPLATE.textWithClass(raceColumnName, STYLE_INACTIVE_RACE));
}
}
private SafeHtml getAnchor(String link, String linkText, String style) {
if (isEmbedded) {
return ANCHORTEMPLATE.anchorWithTarget(link, linkText, style, "_blank");
return ANCHORTEMPLATE.anchorWithTarget(UriUtils.fromString(link), linkText, style, "_blank");
} else {
return ANCHORTEMPLATE.anchor(link, linkText, style);
return ANCHORTEMPLATE.anchor(UriUtils.fromString(link), linkText, style);
}
}
@@ -46,4 +46,5 @@
<inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/>
<entry-point class="com.sap.sailing.gwt.ui.datamining.DataMiningEntryPoint" />
</module>
@@ -1,5 +1,6 @@
package com.sap.sse.datamining.shared.impl.dto;
import java.io.Serializable;
import java.util.Map;
import com.sap.sse.datamining.shared.AdditionalResultData;
@@ -9,7 +10,8 @@ import com.sap.sse.datamining.shared.data.QueryResultState;
import com.sap.sse.datamining.shared.impl.NullAdditionalResultData;
import com.sap.sse.datamining.shared.impl.QueryResultBaseImpl;
public class QueryResultDTO<ResultType> extends QueryResultBaseImpl<ResultType> implements QueryResultBase<ResultType> {
public class QueryResultDTO<ResultType extends Serializable> extends QueryResultBaseImpl<ResultType>
implements QueryResultBase<ResultType> {
private static final long serialVersionUID = 3639302996859873603L;
private String resultTypeName;
@@ -1,5 +1,6 @@
package com.sap.sse.datamining.factories;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -102,7 +103,8 @@ public class DataMiningDTOFactory {
localizedRetrievedDataType, retrieverLevel.getDefaultSettings());
}
public <ResultType> QueryResultDTO<ResultType> createResultDTO(QueryResult<ResultType> result) {
public <ResultType extends Serializable> QueryResultDTO<ResultType> createResultDTO(
QueryResult<ResultType> result) {
return new QueryResultDTO<ResultType>(result.getState(), result.getResultType(), result.getResults(), result.getAdditionalData());
}
@@ -23,7 +23,7 @@ body {
background-color: #333333;
border-bottom: 4px solid #f0ab00;
color: white;
position: relative
position: relative;
}
.registrationform_header_appname {
@@ -32,7 +32,7 @@ body {
margin: 0;
position: absolute;
top: 50%;
transform: translate(0, -50%)
transform: translate(0, -50%);
}
.registrationform_content {