<li>Declarative layout support for initializing a component hierarchy from an HTML file.</li>
<li>Uses GWT 2.7 for improved compilation times when using Super Dev Mode.</li>
<li>@Viewport annotation for declaratively defining a mobile viewport definition for a UI.</li>
- <li>Captions can be configured to be displayed as HTML.</li>
+ <li>Component captions, TabSheet/Accordion tab captions and Calendar event captions can be configured to be displayed as HTML.</li>
<li>Selects use converters when presenting itemids.</li>
<li>Improved performance when server response contains no visual changing (e.g. empty polling responses).</li>
<li>Unified JSON library for using the same API in both server-side and client-side code.</li>
<p>Raw JSON values passed to AbstractJavaScriptComponent.callFunction and AbstractJavaScriptExtension.callFunction should be changed to use elemental.json types.</p>
</li>
<li>The semantics of empty and required for Field classes has been made more consistent. This mainly affects Checkbox which is now considered to be empty when it is not checked.</li>
+ <li>The previously inconsistent behavior in HTML vs plain text rendering of Calendar event captions has been made consistent.</li>
<li>Support for Opera 12 has been dropped. Newer versions based on the Blink rendering engine are still supported.</li>
</ul>
<h3 id="knownissues">Known issues</h3>
private MouseEventListener mouseEventListener;
private boolean forwardNavigationEnabled = true;
private boolean backwardNavigationEnabled = true;
+ private boolean eventCaptionAsHtml = false;
/**
* Get the listener that listen to mouse events
public void setDropHandler(CalendarDropHandler dropHandler) {
this.dropHandler = dropHandler;
}
+
+ /**
+ * Sets whether the event captions are rendered as HTML.
+ * <p>
+ * If set to true, the captions are rendered in the browser as HTML and the
+ * developer is responsible for ensuring no harmful HTML is used. If set to
+ * false, the caption is rendered in the browser as plain text.
+ * <p>
+ * The default is false, i.e. to render that caption as plain text.
+ *
+ * @param captionAsHtml
+ * true if the captions are rendered as HTML, false if rendered
+ * as plain text
+ */
+ public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) {
+ this.eventCaptionAsHtml = eventCaptionAsHtml;
+ }
+
+ /**
+ * Checks whether event captions are rendered as HTML
+ * <p>
+ * The default is false, i.e. to render that caption as plain text.
+ *
+ * @return true if the captions are rendered as HTML, false if rendered as
+ * plain text
+ */
+ public boolean isEventCaptionAsHtml() {
+ return eventCaptionAsHtml;
+ }
}
widget.setEventMoveAllowed(hasEventListener(CalendarEventId.EVENTMOVE));
widget.setEventResizeAllowed(hasEventListener(CalendarEventId.EVENTRESIZE));
+ widget.setEventCaptionAsHtml(state.eventCaptionAsHtml);
+
List<CalendarState.Day> days = state.days;
List<CalendarState.Event> events = state.events;
*/
private void updateCaptions(boolean bigMode) {
String innerHtml;
- String escapedCaption = Util.escapeHTML(calendarEvent.getCaption());
String timeAsText = calendarEvent.getTimeAsText();
+ String htmlOrText;
+
+ if (dateCell.weekgrid.getCalendar().isEventCaptionAsHtml()) {
+ htmlOrText = calendarEvent.getCaption();
+ } else {
+ htmlOrText = Util.escapeHTML(calendarEvent.getCaption());
+ }
+
if (bigMode) {
- innerHtml = "<span>" + timeAsText + "</span><br />"
- + escapedCaption;
+ innerHtml = "<span>" + timeAsText + "</span><br />" + htmlOrText;
} else {
innerHtml = "<span>" + timeAsText + "<span>:</span></span> "
- + escapedCaption;
+ + htmlOrText;
}
caption.setInnerHTML(innerHtml);
eventContent.setInnerHTML("");
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.user.client.ui.HTML;
+import com.vaadin.client.Util;
import com.vaadin.client.ui.VCalendar;
/**
* Set the caption of the event label
*
* @param caption
- * The caption string, can be HTML
+ * The caption string, can be HTML if
+ * {@link VCalendar#isEventCaptionAsHtml()} is true
*/
public void setCaption(String caption) {
this.caption = caption;
*/
private void renderCaption() {
StringBuilder html = new StringBuilder();
+ String textOrHtml;
+ if (calendar.isEventCaptionAsHtml()) {
+ textOrHtml = caption;
+ } else {
+ textOrHtml = Util.escapeHTML(caption);
+ }
+
if (caption != null && time != null) {
html.append("<span class=\"" + STYLENAME + "-time\">");
html.append(calendar.getTimeFormat().format(time));
html.append("</span> ");
- html.append(caption);
+ html.append(textOrHtml);
} else if (caption != null) {
- html.append(caption);
+ html.append(textOrHtml);
} else if (time != null) {
html.append("<span class=\"" + STYLENAME + "-time\">");
html.append(calendar.getTimeFormat().format(time));
eventLabel.addStyleDependentName(extraStyle + "-all-day");
}
if (!started) {
- eventLabel.setText(calendarEvent.getCaption());
+ if (calendar.isEventCaptionAsHtml()) {
+ eventLabel.setHTML(calendarEvent.getCaption());
+ } else {
+ eventLabel.setText(calendarEvent.getCaption());
+ }
started = true;
}
}
return (CalendarState) super.getState();
}
+ @Override
+ protected CalendarState getState(boolean markAsDirty) {
+ return (CalendarState) super.getState(markAsDirty);
+ }
+
@Override
public void beforeClientResponse(boolean initial) {
super.beforeClientResponse(initial);
* weekly mode
*/
public boolean isMonthlyMode() {
- CalendarState state = (CalendarState) getState(false);
+ CalendarState state = getState(false);
if (state.days != null) {
return state.days.size() > 7;
} else {
dropHandler.getAcceptCriterion().paint(target);
}
}
+
+ /**
+ * Sets whether the event captions are rendered as HTML.
+ * <p>
+ * If set to true, the captions are rendered in the browser as HTML and the
+ * developer is responsible for ensuring no harmful HTML is used. If set to
+ * false, the caption is rendered in the browser as plain text.
+ * <p>
+ * The default is false, i.e. to render that caption as plain text.
+ *
+ * @param captionAsHtml
+ * true if the captions are rendered as HTML, false if rendered
+ * as plain text
+ */
+ public void setEventCaptionAsHtml(boolean eventCaptionAsHtml) {
+ getState().eventCaptionAsHtml = eventCaptionAsHtml;
+ }
+
+ /**
+ * Checks whether event captions are rendered as HTML
+ * <p>
+ * The default is false, i.e. to render that caption as plain text.
+ *
+ * @return true if the captions are rendered as HTML, false if rendered as
+ * plain text
+ */
+ public boolean isEventCaptionAsHtml() {
+ return getState(false).eventCaptionAsHtml;
+ }
+
}
public List<CalendarState.Day> days;
public List<CalendarState.Event> events;
public List<CalendarState.Action> actions;
+ public boolean eventCaptionAsHtml;
public static class Day implements java.io.Serializable {
public String date;
--- /dev/null
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.util.MethodProperty;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Calendar;
+import com.vaadin.ui.CheckBox;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.NativeSelect;
+import com.vaadin.ui.components.calendar.event.BasicEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEvent;
+import com.vaadin.ui.components.calendar.event.CalendarEventProvider;
+
+public class CalendarHtmlInEvents extends AbstractTestUIWithLog {
+
+ private Calendar calendar = new Calendar();
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ final NativeSelect ns = new NativeSelect("Period");
+ ns.addItems("Day", "Week", "Month");
+ ns.addValueChangeListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ if ("Day".equals(ns.getValue())) {
+ calendar.setStartDate(new Date(2014 - 1900, 1 - 1, 1));
+ calendar.setEndDate(new Date(2014 - 1900, 1 - 1, 1));
+ } else if ("Week".equals(ns.getValue())) {
+ calendar.setStartDate(new Date(2014 - 1900, 1 - 1, 1));
+ calendar.setEndDate(new Date(2014 - 1900, 1 - 1, 7));
+ } else if ("Month".equals(ns.getValue())) {
+ calendar.setStartDate(new Date(2014 - 1900, 1 - 1, 1));
+ calendar.setEndDate(new Date(2014 - 1900, 2 - 1, 1));
+ }
+ }
+ });
+ ns.setValue("Month");
+ final CheckBox allowHtml = new CheckBox("Allow HTML in event caption",
+ new MethodProperty<Boolean>(calendar, "eventCaptionAsHtml"));
+ allowHtml.addValueChangeListener(new ValueChangeListener() {
+ @Override
+ public void valueChange(ValueChangeEvent event) {
+ log("HTML in event caption: " + allowHtml.getValue());
+ }
+ });
+ HorizontalLayout hl = new HorizontalLayout();
+ hl.setDefaultComponentAlignment(Alignment.BOTTOM_LEFT);
+ hl.addComponents(ns, allowHtml);
+ hl.setSpacing(true);
+ hl.setMargin(true);
+ calendar.setEventProvider(new CalendarEventProvider() {
+
+ @Override
+ public List<CalendarEvent> getEvents(Date startDate, Date endDate) {
+ Date d = startDate;
+ ArrayList<CalendarEvent> events = new ArrayList<CalendarEvent>();
+ while (d.before(endDate)) {
+ BasicEvent ce = new BasicEvent();
+ ce.setAllDay(false);
+ ce.setCaption("<b>Hello</b> <u>world</u>!");
+ ce.setDescription("Nothing really important");
+ Date start = new Date(d.getTime());
+ start.setHours(d.getDay());
+ Date end = new Date(d.getTime());
+ end.setHours(d.getDay() + 3);
+ ce.setStart(start);
+ ce.setEnd(end);
+ events.add(ce);
+ d.setTime(d.getTime() + 1000 * 60 * 60 * 24);
+ }
+
+ return events;
+ }
+
+ });
+ addComponent(hl);
+ addComponent(calendar);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2014 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.tests.components.calendar;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import com.vaadin.testbench.elements.CalendarElement;
+import com.vaadin.testbench.elements.CheckBoxElement;
+import com.vaadin.testbench.elements.NativeSelectElement;
+import com.vaadin.tests.tb3.SingleBrowserTest;
+
+public class CalendarHtmlInEventsTest extends SingleBrowserTest {
+
+ private NativeSelectElement periodSelect;
+ private CheckBoxElement htmlAllowed;
+ private CalendarElement calendar;
+
+ @Override
+ public void setup() throws Exception {
+ super.setup();
+ openTestURL();
+ periodSelect = $(NativeSelectElement.class).first();
+ htmlAllowed = $(CheckBoxElement.class).first();
+ calendar = $(CalendarElement.class).first();
+ }
+
+ @Test
+ public void monthViewEventCaptions() {
+ Assert.assertEquals(getMonthEvent(0).getText(),
+ "12:00 AM <b>Hello</b> <u>world</u>!");
+
+ // Switch to HTML mode
+ click(htmlAllowed);
+ Assert.assertEquals("1. HTML in event caption: true", getLogRow(0));
+
+ Assert.assertEquals(getMonthEvent(0).getText(), "12:00 AM Hello world!");
+ }
+
+ @Test
+ public void weekViewEventCaptions() {
+ periodSelect.selectByText("Week");
+ Assert.assertEquals("4:00 AM\n<b>Hello</b> <u>world</u>!",
+ getWeekEvent(1).getText());
+
+ // Switch to HTML mode
+ click(htmlAllowed);
+ Assert.assertEquals("1. HTML in event caption: true", getLogRow(0));
+
+ Assert.assertEquals("4:00 AM\nHello world!", getWeekEvent(1).getText());
+ }
+
+ @Test
+ public void dayViewEventCaptions() {
+ periodSelect.selectByText("Day");
+ Assert.assertEquals("3:00 AM\n<b>Hello</b> <u>world</u>!",
+ getWeekEvent(0).getText());
+
+ // Switch to HTML mode
+ click(htmlAllowed);
+ Assert.assertEquals("1. HTML in event caption: true", getLogRow(0));
+ Assert.assertEquals("3:00 AM\nHello world!", getWeekEvent(0).getText());
+ }
+
+ private WebElement getMonthEvent(int dayInCalendar) {
+ return getMonthDay(dayInCalendar).findElement(
+ By.className("v-calendar-event"));
+ }
+
+ private WebElement getWeekEvent(int dayInCalendar) {
+ return getWeekDay(dayInCalendar).findElement(
+ By.className("v-calendar-event"));
+ }
+
+ private void click(CheckBoxElement htmlAllowed2) {
+ htmlAllowed2.findElement(By.xpath("input")).click();
+ }
+
+ private WebElement getMonthDay(int i) {
+ return calendar.findElements(By.className("v-calendar-month-day")).get(
+ i);
+ }
+
+ private WebElement getWeekDay(int i) {
+ return calendar.findElements(By.className("v-calendar-day-times")).get(
+ i);
+ }
+}