The default timeout of 10 seconds for the lookup of other elements.
+ * The default timeout of 15 seconds for the lookup of other elements.
*/
- protected static final int DEFAULT_LOOKUP_TIMEOUT = 5;
+ protected static final int DEFAULT_LOOKUP_TIMEOUT = 60;
/**
*
The web driver to use.
diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventCreateDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventCreateDialogPO.java
index af22c31ff14..d7e30d0eb45 100644
--- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventCreateDialogPO.java
+++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventCreateDialogPO.java
@@ -9,8 +9,8 @@ import org.openqa.selenium.support.ui.Select;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
-import com.sap.sailing.selenium.pages.gwt.BetterDateTimeBoxPO;
import com.sap.sailing.selenium.pages.gwt.CheckBoxPO;
+import com.sap.sailing.selenium.pages.gwt.DateAndTimeInputPO;
import com.sap.sailing.selenium.pages.gwt.TextAreaPO;
import com.sap.sailing.selenium.pages.gwt.TextBoxPO;
@@ -50,8 +50,8 @@ public class EventCreateDialogPO extends DataEntryDialogPO {
TextBoxPO.create(driver, nameTextBox).appendText(name);
TextAreaPO.create(driver, descriptionTextArea).appendText(description);
TextBoxPO.create(driver, venueTextBox).appendText(venue);
- BetterDateTimeBoxPO.create(driver, startDateTimeBox).setDate(start);
- BetterDateTimeBoxPO.create(driver, endDateTimeBox).setDate(end);
+ DateAndTimeInputPO.create(driver, startDateTimeBox).setValue(start, false);
+ DateAndTimeInputPO.create(driver, endDateTimeBox).setValue(end, false);
CheckBoxPO.create(driver, isPublicCheckBox).setSelected(isPublic);
}
diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/SetStartTimeDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/SetStartTimeDialogPO.java
index 7150af20da3..bce2218d05c 100644
--- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/SetStartTimeDialogPO.java
+++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/leaderboard/SetStartTimeDialogPO.java
@@ -8,7 +8,7 @@ import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
-import com.sap.sailing.selenium.pages.gwt.BetterDateTimeBoxPO;
+import com.sap.sailing.selenium.pages.gwt.DateAndTimeInputPO;
public class SetStartTimeDialogPO extends DataEntryDialogPO {
@@ -28,7 +28,7 @@ public class SetStartTimeDialogPO extends DataEntryDialogPO {
}
public void setStartTimeValue(Date startTime) {
- BetterDateTimeBoxPO.create(driver, startTimeTimeBox, true).setDate(startTime);
+ DateAndTimeInputPO.create(driver, startTimeTimeBox).setValue(startTime, true);
}
public void pressSetStartTime() {
diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java
index faae30a507a..b713b005dc6 100644
--- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java
+++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/regatta/RegattaCreateDialogPO.java
@@ -8,7 +8,7 @@ import org.openqa.selenium.WebElement;
import com.sap.sailing.selenium.core.BySeleniumId;
import com.sap.sailing.selenium.core.FindBy;
import com.sap.sailing.selenium.pages.common.DataEntryDialogPO;
-import com.sap.sailing.selenium.pages.gwt.BetterDateTimeBoxPO;
+import com.sap.sailing.selenium.pages.gwt.DateAndTimeInputPO;
import com.sap.sailing.selenium.pages.gwt.ListBoxPO;
import com.sap.sailing.selenium.pages.gwt.TextBoxPO;
@@ -56,8 +56,8 @@ public class RegattaCreateDialogPO extends DataEntryDialogPO {
public void setValues(String name, String boatClass, Date startDate, Date endDate) {
this.setRegattaName(name);
this.setBoatClass(boatClass);
- BetterDateTimeBoxPO.create(driver, startDateTimeBox).setDate(startDate);
- BetterDateTimeBoxPO.create(driver, endDateTimeBox).setDate(endDate);
+ DateAndTimeInputPO.create(driver, startDateTimeBox).setValue(startDate, false);
+ DateAndTimeInputPO.create(driver, endDateTimeBox).setValue(endDate, false);
}
// TODO: Scoring System, Event and Course Area
diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/gwt/BetterDateTimeBoxPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/gwt/BetterDateTimeBoxPO.java
deleted file mode 100644
index 6baa9dbfe0a..00000000000
--- a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/gwt/BetterDateTimeBoxPO.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.sap.sailing.selenium.pages.gwt;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import org.openqa.selenium.WebDriver;
-import org.openqa.selenium.WebElement;
-
-import com.sap.sailing.selenium.pages.PageObject;
-
-/**
- * {@link PageObject} implementation for better date time boxes.
- */
-public class BetterDateTimeBoxPO extends TextBoxPO {
-
- private static final DateFormat TIME_FORMAT_HOURS_MINUTES = new SimpleDateFormat("dd/MM/yyyy hh:mm");
- private static final DateFormat TIME_FORMAT_HOURS_MINUTES_SECONDS = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
-
- private final DateFormat timeFormat;
-
- /**
- * Factory method to create a {@link BetterDateTimeBoxPO} using {@link DateFormat} dd/MM/yyyy hh:mm by
- * default.
- *
- * @param driver the web driver to use
- * @param element the element representing the date time box on the page
- * @return a new {@link BetterDateTimeBoxPO} instance
- */
- public static BetterDateTimeBoxPO create(WebDriver driver, WebElement element) {
- return new BetterDateTimeBoxPO(driver, element, TIME_FORMAT_HOURS_MINUTES);
- }
- /**
- * Factory method to create a {@link BetterDateTimeBoxPO}.
- *
- * @param driver the web driver to use
- * @param element the element representing the date time box on the page
- * @param includeSeconds true to use the {@link DateFormat} dd/MM/yyyy hh:mm:ss,
- * false to use the default {@link DateFormat} dd/MM/yyyy hh:mm
- * @return a new {@link BetterDateTimeBoxPO} instance
- */
- public static BetterDateTimeBoxPO create(WebDriver driver, WebElement element, boolean includeSeconds) {
- DateFormat timeFormat = includeSeconds ? TIME_FORMAT_HOURS_MINUTES_SECONDS : TIME_FORMAT_HOURS_MINUTES;
- return new BetterDateTimeBoxPO(driver, element, timeFormat);
- }
-
- /**
- * @see TextBoxPO#TextBoxPO(WebDriver, WebElement)
- */
- protected BetterDateTimeBoxPO(WebDriver driver, WebElement element, DateFormat timeFormat) {
- super(driver, element);
- this.timeFormat = timeFormat;
- }
-
- /**
- * Sets the underlying {@link WebElement}s text by formatting the given {@link Date} using the internal date format
- * (dd/MM/yyyy hh:mm or dd/MM/yyyy hh:mm:ss).
- *
- * @param date the {@link Date} object to set
- *
- * @see DateFormat#format(Date)
- * @see #setText(String)
- */
- public void setDate(Date date) {
- this.setText(timeFormat.format(date));
- }
-
- @Override
- public void appendText(String text) {
- super.appendText(text);
- // Send tabulator key after changing text to ensure the date time boxes datepicker popup is closed.
- // Otherwise, the popup potentially overlaps other controls, which can cause unexpected errors.
- super.appendText("\t");
- }
-
-}
diff --git a/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/gwt/DateAndTimeInputPO.java b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/gwt/DateAndTimeInputPO.java
new file mode 100644
index 00000000000..51e005687f3
--- /dev/null
+++ b/java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/gwt/DateAndTimeInputPO.java
@@ -0,0 +1,56 @@
+package com.sap.sailing.selenium.pages.gwt;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+import com.sap.sailing.selenium.core.BySeleniumId;
+import com.sap.sailing.selenium.core.FindBy;
+import com.sap.sailing.selenium.pages.PageArea;
+
+public class DateAndTimeInputPO extends PageArea {
+
+ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("MMM d, y");
+ private static final DateFormat TIME_FORMAT_MINUTES = new SimpleDateFormat("h:mm a");
+ private static final DateFormat TIME_FORMAT_SECONDS = new SimpleDateFormat("h:mm:ss a");
+
+ @FindBy(how = BySeleniumId.class, using = "dateInput")
+ private WebElement dateInput;
+
+ @FindBy(how = BySeleniumId.class, using = "timeInput")
+ private WebElement timeInput;
+
+ private DateAndTimeInputPO(WebDriver driver, WebElement element) {
+ super(driver, element);
+ }
+
+ /**
+ * Sets the provided {@link Date date value} to the underlying {@link WebElement}.
+ *
+ * @param date
+ * the {@link Date date value} to set to the input field
+ * @param enterSeconds
+ * true to enter seconds fragment as well, false otherwise
+ *
+ * @see DateFormat#format(Date)
+ * @see WebElement#sendKeys(CharSequence...)
+ */
+ public void setValue(Date date, boolean enterSeconds) {
+ this.setValue(dateInput, date, DATE_FORMAT);
+ this.setValue(timeInput, date, enterSeconds ? TIME_FORMAT_SECONDS : TIME_FORMAT_MINUTES);
+ }
+
+ private void setValue(WebElement input, Date date, DateFormat format) {
+ final String value = format.format(date);
+ input.clear();
+ input.sendKeys(value);
+ input.sendKeys("\t"); // ensure popup closing!
+ }
+
+ public static DateAndTimeInputPO create(WebDriver driver, WebElement element) {
+ return new DateAndTimeInputPO(driver, element);
+ }
+}
diff --git a/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css b/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css
index 4f765a519e3..52cd9c16ee9 100644
--- a/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css
+++ b/java/com.sap.sailing.www/clean/sailing-clean-1.0.cache.css
@@ -124,7 +124,7 @@ select {
background: #F1F1F1;
padding: 4px 8px 4px 4px;
cursor: default;
- font-family: Arial Unicode MS, Arial, sans-serif;
+ font-family: 'Open sans', Arial Unicode MS, Arial, sans-serif;
font-weight: bold;
border-bottom: 1px solid #bbbbbb;
border-top: 1px solid #D2D2D2;
@@ -269,7 +269,7 @@ select {
}
.gwt-MenuBar .gwt-MenuItem {
cursor: default;
- font-family: Arial Unicode MS, Arial, sans-serif;
+ font-family: 'Open sans', Arial Unicode MS, Arial, sans-serif;
}
.gwt-MenuBar .gwt-MenuItem-selected {
background: #E3E8F3;
@@ -722,7 +722,7 @@ select {
border: 1px solid #ccc;
border-top: 1px solid #999;
font-size: 100%;
- font-family: Arial Unicode MS, Arial, sans-serif;
+ font-family: 'Open sans', Arial Unicode MS, Arial, sans-serif;
}
.gwt-SuggestBoxPopup {
@@ -959,7 +959,7 @@ select {
border: 1px solid #ccc;
border-top: 1px solid #666;
font-size: 100%;
- font-family: Arial Unicode MS, Arial, sans-serif;
+ font-family: 'Open sans', Arial Unicode MS, Arial, sans-serif;
}
.gwt-TextArea-readonly {
color: #888;
@@ -970,7 +970,7 @@ select {
border: 1px solid #ccc;
border-top: 1px solid #999;
font-size: small;
- font-family: Arial Unicode MS, Arial, sans-serif;
+ font-family: 'Open sans', Arial Unicode MS, Arial, sans-serif;
}
.gwt-TextBox-readonly {
color: #888;
diff --git a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF
index 2063feaaa13..16f6dc7fbba 100644
--- a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF
+++ b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF
@@ -28,9 +28,11 @@ Export-Package: com.google.gwt.user.client.rpc.core.java.util,
com.sap.sse.gwt.client.controls,
com.sap.sse.gwt.client.controls.busyindicator,
com.sap.sse.gwt.client.controls.carousel,
+ com.sap.sse.gwt.client.controls.datetime,
com.sap.sse.gwt.client.controls.filestorage,
com.sap.sse.gwt.client.controls.languageselect,
com.sap.sse.gwt.client.controls.listedit,
+ com.sap.sse.gwt.client.controls.progressbar,
com.sap.sse.gwt.client.controls.slider,
com.sap.sse.gwt.client.dialog,
com.sap.sse.gwt.client.event,
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/AbstractInput.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/AbstractInput.java
new file mode 100644
index 00000000000..7fe34224efe
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/AbstractInput.java
@@ -0,0 +1,56 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import java.util.Date;
+
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HasValue;
+import com.google.gwt.user.client.ui.IsWidget;
+
+/**
+ * Abstract implementation for classes wrapping date and/or time inputs providing a fallback widget if the respective
+ * native input type is not supported.
+ */
+abstract class AbstractInput extends Composite implements DateTimeInput {
+
+ private final HasValue delegate;
+
+ /**
+ * Creates a new {@link AbstractInput} instance passing method calls to the provided delegate.
+ *
+ * @param delegate
+ * the native or fallback input for the respective input type
+ */
+ protected & IsWidget> AbstractInput(T delegate) {
+ DateTimeInputResources.INSTANCE.css().ensureInjected();
+ this.delegate = delegate;
+ initWidget(delegate.asWidget());
+ }
+
+ @Override
+ protected final void onEnsureDebugId(String baseId) {
+ getWidget().ensureDebugId(baseId);
+ }
+
+ @Override
+ public final Date getValue() {
+ return delegate.getValue();
+ }
+
+ @Override
+ public final void setValue(Date value) {
+ this.delegate.setValue(value);
+ }
+
+ @Override
+ public void setValue(Date value, boolean fireEvents) {
+ this.delegate.setValue(value, fireEvents);
+ }
+
+ @Override
+ public final HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) {
+ return delegate.addValueChangeHandler(handler);
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateAndTimeInput.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateAndTimeInput.java
new file mode 100644
index 00000000000..d851167a205
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateAndTimeInput.java
@@ -0,0 +1,84 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.ui.FlowPanel;
+
+/**
+ * Extension of {@link AbstractInput} for date and time inputs, providing a native input field of type "datetime-local",
+ * if supported. Otherwise a panel containing a {@link DateInput} and a {@link TimeInput} is used as fallback widget.
+ */
+public class DateAndTimeInput extends AbstractInput {
+
+ /**
+ * Created a new {@link DateAndTimeInput} instance which the provided {@link Accuracy accuracy}.
+ *
+ * @param accuracy
+ * {@link Accuracy} for the new input
+ */
+ public DateAndTimeInput(Accuracy accuracy) {
+ super(DateTimeInputType.DATETIME_LOCAL.isSupported() ? NativeDateTimeInput.datetimeLocale(accuracy)
+ : new DateAndTimePanel(accuracy));
+ }
+
+ private static class DateAndTimePanel extends FlowPanel implements DateTimeInput {
+ private final DateInput dateInput;
+ private final TimeInput timeInput;
+
+ private final DateTimeFormat dateFormat, timeFormat, datetimeFormat;
+
+ private DateAndTimePanel(Accuracy accuracy) {
+ add(this.dateInput = new DateInput());
+ add(this.timeInput = new TimeInput(accuracy));
+ this.dateInput.addStyleName(DateTimeInputResources.INSTANCE.css().dateTimeInput());
+ this.timeInput.addStyleName(DateTimeInputResources.INSTANCE.css().dateTimeInput());
+ this.dateFormat = Accuracy.getDateFormat();
+ this.timeFormat = accuracy.getTimeFormat();
+ this.datetimeFormat = accuracy.getDatetimeFormat();
+ }
+
+ @Override
+ protected void onEnsureDebugId(String baseId) {
+ super.onEnsureDebugId(baseId);
+ this.dateInput.ensureDebugId("dateInput");
+ this.timeInput.ensureDebugId("timeInput");
+ }
+
+ @Override
+ public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) {
+ final ValueChangeHandler internalHandler = event -> ValueChangeEvent.fire(this, getValue());
+ final List handlerRegistrations = new ArrayList<>();
+ handlerRegistrations.add(dateInput.addValueChangeHandler(internalHandler));
+ handlerRegistrations.add(timeInput.addValueChangeHandler(internalHandler));
+ handlerRegistrations.add(addHandler(handler, ValueChangeEvent.getType()));
+ return () -> handlerRegistrations.forEach(HandlerRegistration::removeHandler);
+ }
+
+ @Override
+ public Date getValue() {
+ final Date dateValue = dateInput.getValue(), timeValue = timeInput.getValue();
+ if (Objects.nonNull(dateValue) && Objects.nonNull(timeValue)) {
+ final String dateString = dateFormat.format(dateInput.getValue());
+ final String timeString = timeFormat.format(timeInput.getValue());
+ final String dateAndTimeString = dateString + "T" + timeString;
+ return datetimeFormat.parse(dateAndTimeString);
+ }
+ return null;
+ }
+
+ @Override
+ public void setValue(Date value) {
+ this.dateInput.setValue(value);
+ this.timeInput.setValue(value);
+ }
+
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateInput.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateInput.java
new file mode 100644
index 00000000000..6ad8bc92f24
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateInput.java
@@ -0,0 +1,32 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.user.datepicker.client.DateBox;
+
+/**
+ * Extension of {@link AbstractInput} for date inputs, providing a native input field of type "date", if supported.
+ * Otherwise a {@link DateBox} with {@link PredefinedFormat#DATE_MEDIUM medium date format} is used as fallback widget.
+ */
+public class DateInput extends AbstractInput {
+
+ /**
+ * Created a new {@link DateInput} instance.
+ */
+ public DateInput() {
+ super(DateTimeInputType.TIME.isSupported() ? NativeDateTimeInput.date() : new CustomDateBox());
+ }
+
+ private static class CustomDateBox extends DateBox {
+
+ private final DateTimeFormat viewFormat = DateTimeFormat.getFormat(PredefinedFormat.DATE_MEDIUM);
+
+ private CustomDateBox() {
+ setFormat(new DefaultFormat(viewFormat));
+ addStyleName(DateTimeInputResources.INSTANCE.css().dateTimeInput());
+ getElement().setAttribute("placeholder", viewFormat.getPattern());
+ }
+
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInput.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInput.java
new file mode 100644
index 00000000000..a3a81139524
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInput.java
@@ -0,0 +1,72 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import java.util.Date;
+
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.ui.HasValue;
+import com.google.gwt.user.client.ui.IsWidget;
+
+/**
+ * Interface for date and/or time related input widgets holding a {@link Date date} value.
+ *
+ * @see com.google.gwt.user.client.ui.IsWidget
+ * @see com.google.gwt.user.client.ui.HasValue
+ */
+public interface DateTimeInput extends IsWidget, HasValue {
+
+ /**
+ * Enumeration describing the accuracy of an input field of type "datetime-local" or "time".
+ */
+ public enum Accuracy {
+ MINUTES(60, "yyyy-MM-dd'T'HH:mm", "HH:mm"), SECONDS(1, "yyyy-MM-dd'T'HH:mm:ss", "HH:mm:ss");
+
+ private static final DateTimeFormat DATE_FORMAT = DateTimeFormat.getFormat("yyyy-MM-dd");
+
+ private final DateTimeFormat datetimeFormat, timeFormat;
+ private final String step;
+
+ private Accuracy(int step, String datetimeFormat, String timeFormat) {
+ this.step = String.valueOf(step);
+ this.datetimeFormat = DateTimeFormat.getFormat(datetimeFormat);
+ this.timeFormat = DateTimeFormat.getFormat(timeFormat);
+ }
+
+ /**
+ * @return the {@link DateTimeFormat} for date inputs (always the same)
+ */
+ public static DateTimeFormat getDateFormat() {
+ return DATE_FORMAT;
+ }
+
+ /**
+ * @return the {@link DateTimeFormat} for time inputs
+ */
+ public DateTimeFormat getTimeFormat() {
+ return timeFormat;
+ }
+
+ /**
+ * @return the {@link DateTimeFormat} for date and time inputs
+ */
+ public DateTimeFormat getDatetimeFormat() {
+ return datetimeFormat;
+ }
+
+ /**
+ * @return the "step" attribute value to set on input fields of type "datetime-local" or "time"
+ */
+ public String getStep() {
+ return step;
+ }
+ }
+
+ @Override
+ default void setValue(Date value, boolean fireEvents) {
+ this.setValue(value);
+ if (fireEvents) {
+ ValueChangeEvent.fire(this, value);
+ }
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInputResources.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInputResources.java
new file mode 100644
index 00000000000..b02e4bf21ac
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInputResources.java
@@ -0,0 +1,18 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.CssResource;
+
+interface DateTimeInputResources extends ClientBundle {
+
+ static final DateTimeInputResources INSTANCE = GWT.create(DateTimeInputResources.class);
+
+ @Source("dateTimeInput.gss")
+ Style css();
+
+ interface Style extends CssResource {
+ String dateTimeInput();
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInputType.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInputType.java
new file mode 100644
index 00000000000..008a7a720bd
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/DateTimeInputType.java
@@ -0,0 +1,50 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+/**
+ * Enumeration of date and/or time related input {@link #getType() types} whose {@link #isSupported() support} in the
+ * currently used browser will be checked at runtime.
+ */
+public enum DateTimeInputType {
+
+ DATETIME_LOCAL("datetime-local"), DATE("date"), TIME("time");
+
+ private final String type;
+ private final boolean supported;
+
+ private DateTimeInputType(String type) {
+ this.type = type;
+ this.supported = checkSupport(type);
+ }
+
+ /**
+ * @return the type string for this {@link DateTimeInputType}
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @return true if this {@link DateTimeInputType} is supported, false otherwise
+ */
+ public boolean isSupported() {
+ return supported;
+ }
+
+ private static final native boolean checkSupport(String type) /*-{
+ var input = document.createElement("input");
+ input.setAttribute("type", type);
+ var desiredType = input.getAttribute('type');
+ var supported = false;
+ if (input.type === desiredType) {
+ supported = true;
+ }
+ input.value = 'Hello world';
+ var helloWorldAccepted = (input.value === 'Hello world');
+ if (helloWorldAccepted) {
+ supported = false;
+ }
+ input.value = '';
+ return supported;
+ }-*/;
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/NativeDateTimeInput.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/NativeDateTimeInput.java
new file mode 100644
index 00000000000..550b4e34ff4
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/NativeDateTimeInput.java
@@ -0,0 +1,110 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import java.util.Date;
+import java.util.Objects;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.event.logical.shared.ValueChangeEvent;
+import com.google.gwt.event.logical.shared.ValueChangeHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Factory class to create native input field of type "{@link #datetimeLocale(Accuracy) datetime-local}",
+ * "{@link #date() date}" or "{@link #time(Accuracy) time}".
+ */
+class NativeDateTimeInput extends Widget implements DateTimeInput {
+
+ private static final String ATTR_TYPE = "type", ATTR_STEP = "step";
+
+ /**
+ * Creates a native input field of type "date".
+ *
+ * @return the newly created instance as {@link DateTimeInput} abstraction
+ */
+ static DateTimeInput date() {
+ return new NativeDateTimeInput(DateTimeInputType.DATE, "", Accuracy.getDateFormat());
+ }
+
+ /**
+ * Creates a native input field of type "time" which the provided {@link Accuracy accuracy}.
+ *
+ * @param accuracy
+ * {@link Accuracy} for the new native input field
+ *
+ * @return the newly created instance as {@link DateTimeInput} abstraction
+ */
+ static DateTimeInput time(Accuracy accuracy) {
+ return new NativeDateTimeInput(DateTimeInputType.TIME, accuracy.getStep(), accuracy.getTimeFormat());
+ }
+
+ /**
+ * Creates a native input field of type "datetime-local" which the provided {@link Accuracy accuracy}.
+ *
+ * @param accuracy
+ * {@link Accuracy} for the new native input field
+ *
+ * @return the newly created instance as {@link DateTimeInput} abstraction
+ */
+ static DateTimeInput datetimeLocale(Accuracy accuracy) {
+ return new NativeDateTimeInput(DateTimeInputType.DATETIME_LOCAL, accuracy.getStep(),
+ accuracy.getDatetimeFormat());
+ }
+
+ private final DateTimeFormat format;
+ private final InputElement input;
+
+ private NativeDateTimeInput(DateTimeInputType inputType, String step, DateTimeFormat format) {
+ this.input = Document.get().createElement(InputElement.TAG).cast();
+ this.input.setAttribute(ATTR_TYPE, inputType.getType());
+ this.input.setAttribute(ATTR_STEP, step);
+ this.input.addClassName(DateTimeInputResources.INSTANCE.css().dateTimeInput());
+ this.format = format;
+ setElement(input);
+ sinkEvents(Event.ONCHANGE | Event.ONBLUR | Event.ONKEYUP);
+ }
+
+ @Override
+ public final void onBrowserEvent(Event event) {
+ // TODO: Cache latest value to avoid multiple ValueChangeEvents
+ final int type = event.getTypeInt();
+ if (type == Event.ONCHANGE || type == Event.ONBLUR || type == Event.ONKEYUP) {
+ ValueChangeEvent.fire(this, getValue());
+ }
+ super.onBrowserEvent(event);
+ }
+
+ @Override
+ public final Date getValue() {
+ try {
+ return this.format.parse(input.getValue());
+ } catch (IllegalArgumentException exc) {
+ try {
+ // at least Google Chrome omits the seconds, if they are 00 in input field
+ if (format == Accuracy.SECONDS.getDatetimeFormat()) {
+ return Accuracy.MINUTES.getDatetimeFormat().parse(input.getValue());
+ }
+ if (format == Accuracy.SECONDS.getTimeFormat()) {
+ return Accuracy.MINUTES.getTimeFormat().parse(input.getValue());
+ }
+ } catch (IllegalArgumentException exc2) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public final void setValue(Date value) {
+ this.input.setValue(Objects.isNull(value) ? null : format.format(value));
+ }
+
+ @Override
+ public final HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) {
+ return addHandler(handler, ValueChangeEvent.getType());
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/TimeInput.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/TimeInput.java
new file mode 100644
index 00000000000..982af90544f
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/TimeInput.java
@@ -0,0 +1,74 @@
+package com.sap.sse.gwt.client.controls.datetime;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Objects;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.InputElement;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
+import com.google.gwt.text.shared.AbstractRenderer;
+import com.google.gwt.text.shared.Parser;
+import com.google.gwt.user.client.ui.ValueBox;
+
+/**
+ * Extension of {@link AbstractInput} for time inputs, providing a native input field of type "time", if supported.
+ * Otherwise a {@link ValueBox} for {@link Date dates} is used as fallback widget using a
+ * {@link PredefinedFormat#TIME_SHORT short} or a {@link PredefinedFormat#TIME_MEDIUM medium} time format.
+ */
+public class TimeInput extends AbstractInput {
+
+ /**
+ * Created a new {@link TimeInput} instance which the provided {@link Accuracy accuracy}.
+ *
+ * @param accuracy
+ * {@link Accuracy} for the new input
+ */
+ public TimeInput(Accuracy accuracy) {
+ super(DateTimeInputType.TIME.isSupported() ? NativeDateTimeInput.time(accuracy)
+ : new TimeBox(new TimeConverter(accuracy)));
+ }
+
+ private static class TimeBox extends ValueBox {
+
+ private TimeBox(TimeConverter converter) {
+ super(Document.get().createElement(InputElement.TAG), converter, converter);
+ addStyleName(DateTimeInputResources.INSTANCE.css().dateTimeInput());
+ getElement().setAttribute("placeholder", converter.viewFormat.getPattern());
+ }
+
+ }
+
+ private static class TimeConverter extends AbstractRenderer implements Parser {
+
+ private static final DateTimeFormat VIEW_FORMAT_SHORT = DateTimeFormat.getFormat(PredefinedFormat.TIME_SHORT);
+ private static final DateTimeFormat VIEW_FORMAT_MEDIUM = DateTimeFormat.getFormat(PredefinedFormat.TIME_MEDIUM);
+ private static final String EMPTY_VALUE = "";
+
+ private final DateTimeFormat viewFormat;
+
+ private TimeConverter(Accuracy accuracy) {
+ this.viewFormat = accuracy == Accuracy.SECONDS ? VIEW_FORMAT_MEDIUM : VIEW_FORMAT_SHORT;
+ }
+
+ @Override
+ public String render(Date object) {
+ return Objects.isNull(object) ? EMPTY_VALUE : viewFormat.format(object);
+ }
+
+ @Override
+ public Date parse(CharSequence text) throws ParseException {
+ if (EMPTY_VALUE.equals(text.toString())) {
+ return null;
+ }
+
+ try {
+ return viewFormat.parse(text.toString());
+ } catch (IllegalArgumentException e) {
+ throw new ParseException(e.getMessage(), 0);
+ }
+ }
+
+ }
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/dateTimeInput.gss b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/dateTimeInput.gss
new file mode 100644
index 00000000000..46b55e777bb
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/datetime/dateTimeInput.gss
@@ -0,0 +1,7 @@
+.dateTimeInput {
+ font-family: 'Open Sans', 'Arial Unicode MS', Arial, sans-serif;
+ font-size: small;
+ border: 1px solid #ccc;
+ border-top: 1px solid #999;
+ padding: 2.75px 3px;
+}
\ No newline at end of file
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/progressbar/CustomProgressBar.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/progressbar/CustomProgressBar.java
new file mode 100644
index 00000000000..7bc421e26c7
--- /dev/null
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/progressbar/CustomProgressBar.java
@@ -0,0 +1,98 @@
+package com.sap.sse.gwt.client.controls.progressbar;
+
+import java.util.Objects;
+
+import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Style.Unit;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Custom widget implementation wrapping an HTML progress tag, providing static factory methods for
+ * {@link CustomProgressBar#determinate() determinate} and {@link CustomProgressBar#indeterminate() indeterminate}
+ * instances as well as getter and setter methods for the {@code max} and {@code value} attributes.
+ */
+public class CustomProgressBar extends Widget {
+
+ private static final String TAG = "progress", ATTR_MAX = "max", ATTR_VALUE = "value";
+
+ /**
+ * Creates a new {@link CustomProgressBar} instance in indeterminate state.
+ *
+ * @return the created {@link CustomProgressBar} instance
+ */
+ public static CustomProgressBar indeterminate() {
+ return new CustomProgressBar();
+ }
+
+ /**
+ * Creates a new {@link CustomProgressBar} instance in determinate state with default {@code max} attribute.
+ *
+ * @return the created {@link CustomProgressBar} instance
+ */
+ public static CustomProgressBar determinate() {
+ final CustomProgressBar progressBar = indeterminate();
+ progressBar.setValue(0.0d);
+ return progressBar;
+ }
+
+ /**
+ * Creates a new {@link CustomProgressBar} instance in determinate state with the provided {@code max} attribute.
+ *
+ * @param max
+ * numeric value for {@code max} attribute
+ * @return the created {@link CustomProgressBar} instance
+ *
+ * @see CustomProgressBar#determinate()
+ */
+ public static CustomProgressBar determinate(double max) {
+ final CustomProgressBar progressBar = determinate();
+ progressBar.setMax(max);
+ return progressBar;
+ }
+
+ private CustomProgressBar() {
+ setElement(Document.get().createElement(TAG));
+ getElement().getStyle().setWidth(100, Unit.PCT);
+ getElement().getStyle().setHeight(2, Unit.EM);
+ }
+
+ /**
+ * @return numeric value of the {@link CustomProgressBar}'s {@code max} attribute
+ */
+ public final double getMax() {
+ return this.getDoubleAttribute(ATTR_MAX);
+ }
+
+ /**
+ * @param max
+ * numeric value for the {@link CustomProgressBar}'s {@code max} attribute
+ */
+ public final void setMax(double max) {
+ this.setDoubleAtrribute(ATTR_MAX, max);
+ }
+
+ /**
+ * @return numeric value of the {@link CustomProgressBar}'s {@code value} attribute
+ */
+ public final double getValue() {
+ return this.getDoubleAttribute(ATTR_VALUE);
+ }
+
+ /**
+ * @param max
+ * numeric value for the {@link CustomProgressBar}'s {@code value} attribute
+ */
+ public final void setValue(double value) {
+ this.setDoubleAtrribute(ATTR_VALUE, value);
+ }
+
+ private double getDoubleAttribute(String attrName) {
+ final String attrValue = getElement().getAttribute(attrName);
+ return (Objects.isNull(attrValue) || attrValue.isEmpty()) ? null : Double.parseDouble(attrValue);
+ }
+
+ private void setDoubleAtrribute(String attrName, double attrValue) {
+ getElement().setAttribute(attrName, Double.toString(attrValue));
+ }
+
+}
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/dialog/DialogUtils.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/dialog/DialogUtils.java
index 4948f2b254b..ce5d1dac36e 100644
--- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/dialog/DialogUtils.java
+++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/dialog/DialogUtils.java
@@ -1,5 +1,6 @@
package com.sap.sse.gwt.client.dialog;
+import com.google.gwt.event.dom.client.HasAllKeyHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
@@ -9,8 +10,8 @@ import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FocusWidget;
public abstract class DialogUtils {
- public static void linkEnterToButton(final Button button, FocusWidget... widgets) {
- for (FocusWidget widget : widgets) {
+ public static void linkEnterToButton(final Button button, HasAllKeyHandlers... widgets) {
+ for (HasAllKeyHandlers widget : widgets) {
widget.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {
@@ -22,8 +23,8 @@ public abstract class DialogUtils {
}
}
- public static void linkEscapeToButton(final Button button, FocusWidget... widgets) {
- for (FocusWidget widget : widgets) {
+ public static void linkEscapeToButton(final Button button, HasAllKeyHandlers... widgets) {
+ for (HasAllKeyHandlers widget : widgets) {
widget.addKeyPressHandler(new KeyPressHandler() {
@Override
public void onKeyPress(KeyPressEvent event) {