tags/7.1.07d9f544
Test for #11396 (merged from 6.8 branch). Depends on a blur event so cannot be autotested with TB.1b18807
Remove pre-loader element after pre-loading, fixes #10863ce9c818
Instead of applying workaround to the root panel, apply it to the sub window content element instead to prevent scrolling of the document when a sub window is removed. #11713 (#10776)3f5d022
Liferay 6.2 compatibility (#11751)ffd1c1b
Don't ignore child component margins in AbstractOrderedLayout (#11553)62ae5e1
More verbose output from running Jetty21d9b67
Add a large number of debug calls to VFilterSelect (disabled by default)bdb7931
Merge test for #11623 to 7.0.19e27a1
Move suggestion popup width calculation from connector to VFilterSelect Change-Id: I2d980126599e55fa5e4f6ec523dca16ba54107b5
@@ -50,8 +50,8 @@ public class ResourceLoader { | |||
* Event fired when a resource has been loaded. | |||
*/ | |||
public static class ResourceLoadEvent { | |||
private ResourceLoader loader; | |||
private String resourceUrl; | |||
private final ResourceLoader loader; | |||
private final String resourceUrl; | |||
private final boolean preload; | |||
/** | |||
@@ -286,25 +286,35 @@ public class ResourceLoader { | |||
// Inject loader element if this is the first time this is preloaded | |||
// AND the resources isn't already being loaded in the normal way | |||
Element element = getPreloadElement(url); | |||
final Element element = getPreloadElement(url); | |||
addOnloadHandler(element, new ResourceLoadListener() { | |||
@Override | |||
public void onLoad(ResourceLoadEvent event) { | |||
fireLoad(event); | |||
Document.get().getBody().removeChild(element); | |||
} | |||
@Override | |||
public void onError(ResourceLoadEvent event) { | |||
fireError(event); | |||
Document.get().getBody().removeChild(element); | |||
} | |||
}, event); | |||
// TODO Remove object when loaded (without causing spinner in FF) | |||
Document.get().getBody().appendChild(element); | |||
} | |||
} | |||
private static Element getPreloadElement(String url) { | |||
/*- | |||
* TODO | |||
* In Chrome, FF: | |||
* <object> does not fire event if resource is 404 -> eternal spinner. | |||
* <img> always fires onerror -> no way to know if it loaded -> eternal spinner | |||
* <script type="text/javascript> fires, but also executes -> not preloading | |||
* <script type="text/cache"> does not fire events | |||
* XHR not tested - should work, probably causes other issues | |||
-*/ | |||
if (BrowserInfo.get().isIE()) { | |||
ScriptElement element = Document.get().createScriptElement(); | |||
element.setSrc(url); |
@@ -205,6 +205,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
*/ | |||
SuggestionPopup() { | |||
super(true, false, true); | |||
debug("VFS.SP: constructor()"); | |||
setOwner(VFilterSelect.this); | |||
menu = new SuggestionMenu(); | |||
setWidget(menu); | |||
@@ -243,6 +244,11 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
final Collection<FilterSelectSuggestion> currentSuggestions, | |||
final int currentPage, final int totalSuggestions) { | |||
if (enableDebug) { | |||
debug("VFS.SP: showSuggestions(" + currentSuggestions + ", " | |||
+ currentPage + ", " + totalSuggestions + ")"); | |||
} | |||
/* | |||
* We need to defer the opening of the popup so that the parent DOM | |||
* has stabilized so we can calculate an absolute top and left | |||
@@ -310,6 +316,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* @param active | |||
*/ | |||
private void setNextButtonActive(boolean active) { | |||
if (enableDebug) { | |||
debug("VFS.SP: setNextButtonActive(" + active + ")"); | |||
} | |||
if (active) { | |||
DOM.sinkEvents(down, Event.ONCLICK); | |||
down.setClassName(VFilterSelect.this.getStylePrimaryName() | |||
@@ -327,6 +336,10 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* @param active | |||
*/ | |||
private void setPrevButtonActive(boolean active) { | |||
if (enableDebug) { | |||
debug("VFS.SP: setPrevButtonActive(" + active + ")"); | |||
} | |||
if (active) { | |||
DOM.sinkEvents(up, Event.ONCLICK); | |||
up.setClassName(VFilterSelect.this.getStylePrimaryName() | |||
@@ -343,6 +356,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* Selects the next item in the filtered selections | |||
*/ | |||
public void selectNextItem() { | |||
debug("VFS.SP: selectNextItem()"); | |||
final MenuItem cur = menu.getSelectedItem(); | |||
final int index = 1 + menu.getItems().indexOf(cur); | |||
if (menu.getItems().size() > index) { | |||
@@ -362,6 +376,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* Selects the previous item in the filtered selections | |||
*/ | |||
public void selectPrevItem() { | |||
debug("VFS.SP: selectPrevItem()"); | |||
final MenuItem cur = menu.getSelectedItem(); | |||
final int index = -1 + menu.getItems().indexOf(cur); | |||
if (index > -1) { | |||
@@ -396,6 +411,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void run() { | |||
debug("VFS.SP.LPS: run()"); | |||
if (pagesToScroll != 0) { | |||
if (!waitingForFilteringResponse) { | |||
/* | |||
@@ -416,6 +432,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
} | |||
public void scrollUp() { | |||
debug("VFS.SP.LPS: scrollUp()"); | |||
if (currentPage + pagesToScroll > 0) { | |||
pagesToScroll--; | |||
cancel(); | |||
@@ -424,6 +441,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
} | |||
public void scrollDown() { | |||
debug("VFS.SP.LPS: scrollDown()"); | |||
if (totalMatches > (currentPage + pagesToScroll + 1) | |||
* pageLength) { | |||
pagesToScroll++; | |||
@@ -443,6 +461,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onBrowserEvent(Event event) { | |||
debug("VFS.SP: onBrowserEvent()"); | |||
if (event.getTypeInt() == Event.ONCLICK) { | |||
final Element target = DOM.eventGetTarget(event); | |||
if (target == up || target == DOM.getChild(up, 0)) { | |||
@@ -476,6 +495,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* Should the paging be turned on? | |||
*/ | |||
public void setPagingEnabled(boolean paging) { | |||
debug("VFS.SP: setPagingEnabled(" + paging + ")"); | |||
if (isPagingEnabled == paging) { | |||
return; | |||
} | |||
@@ -501,6 +521,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void setPosition(int offsetWidth, int offsetHeight) { | |||
debug("VFS.SP: setPosition()"); | |||
int top = -1; | |||
int left = -1; | |||
@@ -579,6 +600,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* @return true if popup was just closed | |||
*/ | |||
public boolean isJustClosed() { | |||
debug("VFS.SP: justClosed()"); | |||
final long now = (new Date()).getTime(); | |||
return (lastAutoClosed > 0 && (now - lastAutoClosed) < 200); | |||
} | |||
@@ -593,6 +615,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onClose(CloseEvent<PopupPanel> event) { | |||
if (enableDebug) { | |||
debug("VFS.SP: onClose(" + event.isAutoClosed() + ")"); | |||
} | |||
if (event.isAutoClosed()) { | |||
lastAutoClosed = (new Date()).getTime(); | |||
} | |||
@@ -608,6 +633,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
*/ | |||
public void updateStyleNames(UIDL uidl, | |||
AbstractComponentState componentState) { | |||
debug("VFS.SP: updateStyleNames()"); | |||
setStyleName(VFilterSelect.this.getStylePrimaryName() | |||
+ "-suggestpopup"); | |||
menu.setStyleName(VFilterSelect.this.getStylePrimaryName() | |||
@@ -643,6 +669,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void execute() { | |||
debug("VFS.SM: delayedImageLoadExecutioner()"); | |||
if (suggestionPopup.isVisible() | |||
&& suggestionPopup.isAttached()) { | |||
setWidth(""); | |||
@@ -660,6 +687,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
*/ | |||
SuggestionMenu() { | |||
super(true); | |||
debug("VFS.SM: constructor()"); | |||
addDomHandler(this, LoadEvent.getType()); | |||
} | |||
@@ -683,6 +711,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
*/ | |||
public void setSuggestions( | |||
Collection<FilterSelectSuggestion> suggestions) { | |||
if (enableDebug) { | |||
debug("VFS.SM: setSuggestions(" + suggestions + ")"); | |||
} | |||
// Reset keyboard selection when contents is updated to avoid | |||
// reusing old, invalid data | |||
setKeyboardSelectedItem(null); | |||
@@ -708,6 +739,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* is made or on a blur event. | |||
*/ | |||
public void doSelectedItemAction() { | |||
debug("VFS.SM: doSelectedItemAction()"); | |||
// do not send a value change event if null was and stays selected | |||
final String enteredItemValue = tb.getText(); | |||
if (nullSelectionAllowed && "".equals(enteredItemValue) | |||
@@ -737,6 +769,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* Triggered after a selection has been made | |||
*/ | |||
public void doPostFilterSelectedItemAction() { | |||
debug("VFS.SM: doPostFilterSelectedItemAction()"); | |||
final MenuItem item = getSelectedItem(); | |||
final String enteredItemValue = tb.getText(); | |||
@@ -833,6 +866,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onLoad(LoadEvent event) { | |||
debug("VFS.SM: onLoad()"); | |||
// Handle icon onload events to ensure shadow is resized | |||
// correctly | |||
delayedImageLoadExecutioner.trigger(); | |||
@@ -840,6 +874,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
} | |||
public void selectFirstItem() { | |||
debug("VFS.SM: selectFirstItem()"); | |||
MenuItem firstItem = getItems().get(0); | |||
selectItem(firstItem); | |||
} | |||
@@ -853,6 +888,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
} | |||
public void selectLastItem() { | |||
debug("VFS.SM: selectLastItem()"); | |||
List<MenuItem> items = getItems(); | |||
MenuItem lastItem = items.get(items.size() - 1); | |||
selectItem(lastItem); | |||
@@ -1145,6 +1181,10 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* Whether to send the options request immediately | |||
*/ | |||
private void filterOptions(int page, String filter, boolean immediate) { | |||
if (enableDebug) { | |||
debug("VFS: filterOptions(" + page + ", " + filter + ", " | |||
+ immediate + ")"); | |||
} | |||
if (filter.equals(lastFilter) && currentPage == page) { | |||
if (!suggestionPopup.isAttached()) { | |||
suggestionPopup.showSuggestions(currentSuggestions, | |||
@@ -1171,10 +1211,12 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
/** For internal use only. May be removed or replaced in the future. */ | |||
public void updateReadOnly() { | |||
debug("VFS: updateReadOnly()"); | |||
tb.setReadOnly(readonly || !textInputEnabled); | |||
} | |||
public void setTextInputEnabled(boolean textInputEnabled) { | |||
debug("VFS: setTextInputEnabled()"); | |||
// Always update styles as they might have been overwritten | |||
if (textInputEnabled) { | |||
removeStyleDependentName(STYLE_NO_INPUT); | |||
@@ -1200,6 +1242,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* the text to set in the text box | |||
*/ | |||
public void setTextboxText(final String text) { | |||
if (enableDebug) { | |||
debug("VFS: setTextboxText(" + text + ")"); | |||
} | |||
tb.setText(text); | |||
} | |||
@@ -1208,6 +1253,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* in the text box if nothing has been entered. | |||
*/ | |||
public void setPromptingOn() { | |||
debug("VFS: setPromptingOn()"); | |||
if (!prompting) { | |||
prompting = true; | |||
addStyleDependentName(CLASSNAME_PROMPT); | |||
@@ -1225,6 +1271,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* The text the text box should contain. | |||
*/ | |||
public void setPromptingOff(String text) { | |||
debug("VFS: setPromptingOff()"); | |||
setTextboxText(text); | |||
if (prompting) { | |||
prompting = false; | |||
@@ -1239,6 +1286,10 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* The suggestion that just got selected. | |||
*/ | |||
public void onSuggestionSelected(FilterSelectSuggestion suggestion) { | |||
if (enableDebug) { | |||
debug("VFS: onSuggestionSelected(" + suggestion.caption + ": " | |||
+ suggestion.key + ")"); | |||
} | |||
updateSelectionWhenReponseIsReceived = false; | |||
currentSuggestion = suggestion; | |||
@@ -1334,7 +1385,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
if (enabled && !readonly) { | |||
int keyCode = event.getNativeKeyCode(); | |||
debug("key down: " + keyCode); | |||
if (enableDebug) { | |||
debug("VFS: key down: " + keyCode); | |||
} | |||
if (waitingForFilteringResponse | |||
&& navigationKeyCodes.contains(keyCode)) { | |||
/* | |||
@@ -1342,18 +1395,25 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* waiting for a response. This avoids flickering, disappearing | |||
* items, wrongly interpreted responses and more. | |||
*/ | |||
debug("Ignoring " + keyCode | |||
+ " because we are waiting for a filtering response"); | |||
if (enableDebug) { | |||
debug("Ignoring " | |||
+ keyCode | |||
+ " because we are waiting for a filtering response"); | |||
} | |||
DOM.eventPreventDefault(DOM.eventGetCurrentEvent()); | |||
event.stopPropagation(); | |||
return; | |||
} | |||
if (suggestionPopup.isAttached()) { | |||
debug("Keycode " + keyCode + " target is popup"); | |||
if (enableDebug) { | |||
debug("Keycode " + keyCode + " target is popup"); | |||
} | |||
popupKeyDown(event); | |||
} else { | |||
debug("Keycode " + keyCode + " target is text field"); | |||
if (enableDebug) { | |||
debug("Keycode " + keyCode + " target is text field"); | |||
} | |||
inputFieldKeyDown(event); | |||
} | |||
} | |||
@@ -1372,6 +1432,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* The KeyDownEvent | |||
*/ | |||
private void inputFieldKeyDown(KeyDownEvent event) { | |||
if (enableDebug) { | |||
debug("VFS: inputFieldKeyDown(" + event.getNativeKeyCode() + ")"); | |||
} | |||
switch (event.getNativeKeyCode()) { | |||
case KeyCodes.KEY_DOWN: | |||
case KeyCodes.KEY_UP: | |||
@@ -1414,6 +1477,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* The KeyDownEvent of the key | |||
*/ | |||
private void popupKeyDown(KeyDownEvent event) { | |||
if (enableDebug) { | |||
debug("VFS: popupKeyDown(" + event.getNativeKeyCode() + ")"); | |||
} | |||
// Propagation of handled events is stopped so other handlers such as | |||
// shortcut key handlers do not also handle the same events. | |||
switch (event.getNativeKeyCode()) { | |||
@@ -1494,6 +1560,9 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onKeyUp(KeyUpEvent event) { | |||
if (enableDebug) { | |||
debug("VFS: onKeyUp(" + event.getNativeKeyCode() + ")"); | |||
} | |||
if (enabled && !readonly) { | |||
switch (event.getNativeKeyCode()) { | |||
case KeyCodes.KEY_ENTER: | |||
@@ -1521,6 +1590,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
* Resets the Select to its initial state | |||
*/ | |||
private void reset() { | |||
debug("VFS: reset()"); | |||
if (currentSuggestion != null) { | |||
String text = currentSuggestion.getReplacementString(); | |||
setPromptingOff(text); | |||
@@ -1543,6 +1613,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onClick(ClickEvent event) { | |||
debug("VFS: onClick()"); | |||
if (textInputEnabled | |||
&& event.getNativeEvent().getEventTarget().cast() == tb | |||
.getElement()) { | |||
@@ -1568,6 +1639,29 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
} | |||
} | |||
/** | |||
* Update minimum width for FilterSelect textarea based on input prompt and | |||
* suggestions. | |||
* <p> | |||
* For internal use only. May be removed or replaced in the future. | |||
*/ | |||
public void updateSuggestionPopupMinWidth() { | |||
// used only to calculate minimum width | |||
String captions = Util.escapeHTML(inputPrompt); | |||
for (FilterSelectSuggestion suggestion : currentSuggestions) { | |||
// Collect captions so we can calculate minimum width for | |||
// textarea | |||
if (captions.length() > 0) { | |||
captions += "|"; | |||
} | |||
captions += Util.escapeHTML(suggestion.getReplacementString()); | |||
} | |||
// Calculate minimum textarea width | |||
suggestionPopupMinWidth = minWidth(captions); | |||
} | |||
/** | |||
* Calculate minimum width for FilterSelect textarea. | |||
* <p> | |||
@@ -1610,6 +1704,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onFocus(FocusEvent event) { | |||
debug("VFS: onFocus()"); | |||
/* | |||
* When we disable a blur event in ie we need to refocus the textfield. | |||
@@ -1648,6 +1743,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void onBlur(BlurEvent event) { | |||
debug("VFS: onBlur()"); | |||
if (BrowserInfo.get().isIE() && preventNextBlurEventInIE) { | |||
/* | |||
@@ -1708,6 +1804,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
@Override | |||
public void focus() { | |||
debug("VFS: focus()"); | |||
focused = true; | |||
if (prompting && !readonly) { | |||
setPromptingOff(""); | |||
@@ -1807,6 +1904,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, | |||
*/ | |||
if (BrowserInfo.get().isIE() && focused) { | |||
preventNextBlurEventInIE = true; | |||
debug("VFS: Going to prevent next blur event on IE"); | |||
} | |||
} | |||
} |
@@ -415,14 +415,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
@Override | |||
public void hide() { | |||
if (vaadinModality) { | |||
hideModalityCurtain(); | |||
} | |||
super.hide(); | |||
// Remove window from windowOrder to avoid references being left | |||
// hanging. | |||
windowOrder.remove(this); | |||
/* | |||
* If the window has a RichTextArea and the RTA is focused at the time | |||
@@ -439,20 +431,28 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, | |||
if (BrowserInfo.get().isIE8()) { | |||
fixIE8FocusCaptureIssue(); | |||
} | |||
if (vaadinModality) { | |||
hideModalityCurtain(); | |||
} | |||
super.hide(); | |||
// Remove window from windowOrder to avoid references being left | |||
// hanging. | |||
windowOrder.remove(this); | |||
} | |||
private void fixIE8FocusCaptureIssue() { | |||
Element e = DOM.createInputText(); | |||
Style elemStyle = e.getStyle(); | |||
elemStyle.setPosition(Position.ABSOLUTE); | |||
elemStyle.setLeft(-10, Unit.PX); | |||
elemStyle.setTop(-10, Unit.PX); | |||
elemStyle.setWidth(0, Unit.PX); | |||
elemStyle.setHeight(0, Unit.PX); | |||
Element rootPanel = RootPanel.getBodyElement(); | |||
rootPanel.appendChild(e); | |||
contentPanel.getElement().appendChild(e); | |||
e.focus(); | |||
rootPanel.removeChild(e); | |||
contentPanel.getElement().removeChild(e); | |||
} | |||
/** For internal use only. May be removed or replaced in the future. */ |
@@ -20,7 +20,6 @@ import java.util.Iterator; | |||
import com.vaadin.client.ApplicationConnection; | |||
import com.vaadin.client.Paintable; | |||
import com.vaadin.client.UIDL; | |||
import com.vaadin.client.Util; | |||
import com.vaadin.client.ui.AbstractFieldConnector; | |||
import com.vaadin.client.ui.SimpleManagedLayout; | |||
import com.vaadin.client.ui.VFilterSelect; | |||
@@ -124,9 +123,6 @@ public class ComboBoxConnector extends AbstractFieldConnector implements | |||
getWidget().totalMatches = 0; | |||
} | |||
// used only to calculate minimum popup width | |||
String captions = Util.escapeHTML(getWidget().inputPrompt); | |||
for (final Iterator<?> i = options.getChildIterator(); i.hasNext();) { | |||
final UIDL optionUidl = (UIDL) i.next(); | |||
final FilterSelectSuggestion suggestion = getWidget().new FilterSelectSuggestion( | |||
@@ -152,12 +148,6 @@ public class ComboBoxConnector extends AbstractFieldConnector implements | |||
getWidget().currentSuggestion = suggestion; | |||
getWidget().setSelectedItemIcon(suggestion.getIconUri()); | |||
} | |||
// Collect captions so we can calculate minimum width for textarea | |||
if (captions.length() > 0) { | |||
captions += "|"; | |||
} | |||
captions += Util.escapeHTML(suggestion.getReplacementString()); | |||
} | |||
if ((!getWidget().waitingForFilteringResponse || getWidget().popupOpenerClicked) | |||
@@ -222,8 +212,8 @@ public class ComboBoxConnector extends AbstractFieldConnector implements | |||
} | |||
} | |||
// Calculate minumum textarea width | |||
getWidget().suggestionPopupMinWidth = getWidget().minWidth(captions); | |||
// Calculate minimum textarea width | |||
getWidget().updateSuggestionPopupMinWidth(); | |||
getWidget().popupOpenerClicked = false; | |||
@@ -503,14 +503,10 @@ public class VAbstractOrderedLayout extends FlowPanel { | |||
// TODO check caption position | |||
if (vertical) { | |||
int size = layoutManager.getOuterHeight(slot | |||
.getWidget().getElement()) | |||
- layoutManager.getMarginHeight(slot | |||
.getWidget().getElement()); | |||
.getWidget().getElement()); | |||
if (slot.hasCaption()) { | |||
size += layoutManager.getOuterHeight(slot | |||
.getCaptionElement()) | |||
- layoutManager.getMarginHeight(slot | |||
.getCaptionElement()); | |||
.getCaptionElement()); | |||
} | |||
if (size > 0) { | |||
totalSize += size; | |||
@@ -518,14 +514,10 @@ public class VAbstractOrderedLayout extends FlowPanel { | |||
} else { | |||
int max = -1; | |||
max = layoutManager.getOuterWidth(slot.getWidget() | |||
.getElement()) | |||
- layoutManager.getMarginWidth(slot | |||
.getWidget().getElement()); | |||
.getElement()); | |||
if (slot.hasCaption()) { | |||
int max2 = layoutManager.getOuterWidth(slot | |||
.getCaptionElement()) | |||
- layoutManager.getMarginWidth(slot | |||
.getCaptionElement()); | |||
.getCaptionElement()); | |||
max = Math.max(max, max2); | |||
} | |||
if (max > 0) { |
@@ -17,6 +17,7 @@ package com.vaadin.server; | |||
import java.io.IOException; | |||
import java.io.Serializable; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
import java.util.Enumeration; | |||
import java.util.Map; | |||
@@ -40,7 +41,7 @@ import javax.portlet.ResourceResponse; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletRequestWrapper; | |||
import com.liferay.portal.kernel.util.PortalClassInvoker; | |||
import com.liferay.portal.kernel.util.PortalClassLoaderUtil; | |||
import com.liferay.portal.kernel.util.PropsUtil; | |||
import com.vaadin.server.communication.PortletDummyRequestHandler; | |||
import com.vaadin.server.communication.PortletUIInitHandler; | |||
@@ -171,19 +172,75 @@ public class VaadinPortlet extends GenericPortlet implements Constants, | |||
return PropsUtil.get(name); | |||
} | |||
/** | |||
* Simplified version of what Liferay PortalClassInvoker did. This is | |||
* used because the API of PortalClassInvoker has changed in Liferay | |||
* 6.2. | |||
* | |||
* This simply uses reflection with Liferay class loader. Parameters are | |||
* Strings to avoid static dependencies and to load all classes with | |||
* Liferay's own class loader. Only static utility methods are | |||
* supported. | |||
* | |||
* This method is for internal use only and may change in future | |||
* versions. | |||
* | |||
* @param className | |||
* name of the Liferay class to call | |||
* @param methodName | |||
* name of the method to call | |||
* @param parameterClassName | |||
* name of the parameter class of the method | |||
* @throws Exception | |||
* @return return value of the invoked method | |||
*/ | |||
private static Object invokeStaticLiferayMethod(String className, | |||
String methodName, Object argument, String parameterClassName) | |||
throws Exception { | |||
Thread currentThread = Thread.currentThread(); | |||
ClassLoader contextClassLoader = currentThread | |||
.getContextClassLoader(); | |||
try { | |||
// this should be available across all Liferay versions with no | |||
// problematic static dependencies | |||
ClassLoader portalClassLoader = PortalClassLoaderUtil | |||
.getClassLoader(); | |||
// this is in case the class loading triggers code that | |||
// explicitly | |||
// uses current thread class loader | |||
currentThread.setContextClassLoader(portalClassLoader); | |||
Class<?> targetClass = portalClassLoader.loadClass(className); | |||
Class<?> parameterClass = portalClassLoader | |||
.loadClass(parameterClassName); | |||
Method method = targetClass.getMethod(methodName, | |||
parameterClass); | |||
return method.invoke(null, new Object[] { argument }); | |||
} catch (InvocationTargetException ite) { | |||
throw (Exception) ite.getCause(); | |||
} finally { | |||
currentThread.setContextClassLoader(contextClassLoader); | |||
} | |||
} | |||
private static HttpServletRequest getOriginalRequest( | |||
PortletRequest request) { | |||
try { | |||
// httpRequest = PortalUtil.getHttpServletRequest(request); | |||
HttpServletRequest httpRequest = (HttpServletRequest) PortalClassInvoker | |||
.invoke("com.liferay.portal.util.PortalUtil", | |||
"getHttpServletRequest", request); | |||
HttpServletRequest httpRequest = (HttpServletRequest) invokeStaticLiferayMethod( | |||
"com.liferay.portal.util.PortalUtil", | |||
"getHttpServletRequest", request, | |||
"javax.portlet.PortletRequest"); | |||
// httpRequest = | |||
// PortalUtil.getOriginalServletRequest(httpRequest); | |||
httpRequest = (HttpServletRequest) PortalClassInvoker.invoke( | |||
httpRequest = (HttpServletRequest) invokeStaticLiferayMethod( | |||
"com.liferay.portal.util.PortalUtil", | |||
"getOriginalServletRequest", httpRequest); | |||
"getOriginalServletRequest", httpRequest, | |||
"javax.servlet.http.HttpServletRequest"); | |||
return httpRequest; | |||
} catch (Exception e) { | |||
throw new IllegalStateException("Liferay request not detected", |
@@ -0,0 +1,72 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |||
<head profile="http://selenium-ide.openqa.org/profiles/test-case"> | |||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |||
<link rel="selenium.base" href="http://localhost:8068/" /> | |||
<title>TextChangeListenerLosesFocus</title> | |||
</head> | |||
<body> | |||
<table cellpadding="1" cellspacing="1" border="1"> | |||
<thead> | |||
<tr><td rowspan="1" colspan="3">TextChangeListenerLosesFocus</td></tr> | |||
</thead><tbody> | |||
<tr> | |||
<td>open</td> | |||
<td>/run/com.vaadin.tests.components.textfield.TextChangeListenerLosesFocus?restartApplication</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>focus</td> | |||
<td>test-textfield</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>enterCharacter</td> | |||
<td>test-textfield</td> | |||
<td>123</td> | |||
</tr> | |||
<tr> | |||
<td>pause</td> | |||
<td>1000</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>assertValue</td> | |||
<td>test-textfield</td> | |||
<td>Updated by TextChangeListener</td> | |||
</tr> | |||
<tr> | |||
<td>assertCSSClass</td> | |||
<td>test-textfield</td> | |||
<td>v-textfield-focus</td> | |||
</tr> | |||
<tr> | |||
<td>focus</td> | |||
<td>test-textarea</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>enterCharacter</td> | |||
<td>test-textarea</td> | |||
<td>123</td> | |||
</tr> | |||
<tr> | |||
<td>pause</td> | |||
<td>1000</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>assertValue</td> | |||
<td>test-textarea</td> | |||
<td>Updated by TextChangeListener</td> | |||
</tr> | |||
<tr> | |||
<td>assertCSSClass</td> | |||
<td>test-textarea</td> | |||
<td>v-textarea-focus</td> | |||
</tr> | |||
</tbody></table> | |||
</body> | |||
</html> |
@@ -22,9 +22,20 @@ import java.io.OutputStream; | |||
import java.net.InetAddress; | |||
import java.net.ServerSocket; | |||
import java.net.Socket; | |||
import java.text.SimpleDateFormat; | |||
import java.util.Calendar; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import javax.servlet.Filter; | |||
import javax.servlet.FilterChain; | |||
import javax.servlet.FilterConfig; | |||
import javax.servlet.ServletException; | |||
import javax.servlet.ServletRequest; | |||
import javax.servlet.ServletResponse; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import org.eclipse.jetty.server.Connector; | |||
import org.eclipse.jetty.server.Server; | |||
import org.eclipse.jetty.server.nio.SelectChannelConnector; | |||
@@ -163,6 +174,25 @@ public class DevelopmentServerLauncher { | |||
webappcontext.setWar(serverArgs.get("webroot")); | |||
server.setHandler(webappcontext); | |||
// --slowdown=/run/APP/PUBLISHED/*,/other/path/asd.jpg | |||
// slows down specified paths | |||
if (serverArgs.containsKey("slowdown")) { | |||
String[] paths = serverArgs.get("slowdown").split(","); | |||
for (String p : paths) { | |||
System.out.println("Slowing down: " + p); | |||
webappcontext.addFilter(SlowFilter.class, p, 1); | |||
} | |||
} | |||
// --cache=/run/APP/PUBLISHED/*,/other/path/asd.jpg | |||
// caches specified paths | |||
if (serverArgs.containsKey("cache")) { | |||
String[] paths = serverArgs.get("cache").split(","); | |||
for (String p : paths) { | |||
System.out.println("Enabling cache for: " + p); | |||
webappcontext.addFilter(CacheFilter.class, p, 1); | |||
} | |||
} | |||
try { | |||
server.start(); | |||
@@ -243,4 +273,83 @@ public class DevelopmentServerLauncher { | |||
return map; | |||
} | |||
/** | |||
* Sleeps for 2-5 seconds when serving resources that matches given | |||
* pathSpec. --slowdown=/run/APP/PUBLISHED/*,/other/path/asd.jpg | |||
*/ | |||
public static class SlowFilter implements Filter { | |||
@Override | |||
public void init(FilterConfig filterConfig) throws ServletException { | |||
// TODO Auto-generated method stub | |||
} | |||
@Override | |||
public void doFilter(ServletRequest request, ServletResponse response, | |||
FilterChain chain) throws IOException, ServletException { | |||
String path = ((HttpServletRequest) request).getPathInfo(); | |||
long delay = Math.round(Math.random() * 3000) + 2000; | |||
System.out.println("Delaying " + path + " for " + delay); | |||
try { | |||
Thread.sleep(delay); | |||
} catch (InterruptedException e) { | |||
System.out.println("Delay interrupted for " + path); | |||
} finally { | |||
System.out.println("Resuming " + path); | |||
} | |||
chain.doFilter(request, response); | |||
} | |||
@Override | |||
public void destroy() { | |||
// TODO Auto-generated method stub | |||
} | |||
} | |||
/** | |||
* Adds "Expires" and "Cache-control" headers when serving resources that | |||
* match given pathSpec, in order to cache resource for CACHE_MINUTES. | |||
* --cache=/run/APP/PUBLISHED/*,/other/path/asd.jpg | |||
*/ | |||
public static class CacheFilter implements Filter { | |||
private static final int CACHE_MINUTES = 1; | |||
@Override | |||
public void init(FilterConfig filterConfig) throws ServletException { | |||
// TODO Auto-generated method stub | |||
} | |||
@Override | |||
public void doFilter(ServletRequest request, ServletResponse response, | |||
FilterChain chain) throws IOException, ServletException { | |||
String path = ((HttpServletRequest) request).getPathInfo(); | |||
System.out.println("Caching " + path + " for " + CACHE_MINUTES | |||
+ " minutes"); | |||
Calendar calendar = Calendar.getInstance(); | |||
calendar.add(Calendar.MINUTE, CACHE_MINUTES); | |||
String expires = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z") | |||
.format(calendar.getTime()); | |||
((HttpServletResponse) response).setHeader("Expires", expires); | |||
((HttpServletResponse) response).setHeader("Cache-Control", | |||
"max-age=" + (CACHE_MINUTES * 60)); | |||
chain.doFilter(request, response); | |||
} | |||
@Override | |||
public void destroy() { | |||
// TODO Auto-generated method stub | |||
} | |||
} | |||
} |
@@ -0,0 +1,29 @@ | |||
package com.vaadin.tests.components.textarea; | |||
import com.vaadin.tests.components.TestBase; | |||
import com.vaadin.ui.TextArea; | |||
public class TextDisappearsOnBlur extends TestBase { | |||
@Override | |||
protected void setup() { | |||
TextArea ta = new TextArea(); | |||
addComponent(ta); | |||
// All three are required for the bug to manifest | |||
ta.setMaxLength(50); | |||
ta.setImmediate(true); | |||
ta.setRequired(true); | |||
} | |||
@Override | |||
protected String getDescription() { | |||
return "Text disappears from TextArea in IE 6-8 when focus changes"; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 11396; | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* Copyright 2012 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.textfield; | |||
import com.vaadin.event.FieldEvents.TextChangeEvent; | |||
import com.vaadin.event.FieldEvents.TextChangeListener; | |||
import com.vaadin.tests.components.TestBase; | |||
import com.vaadin.tests.util.TestUtils; | |||
import com.vaadin.ui.AbstractTextField; | |||
import com.vaadin.ui.Field; | |||
import com.vaadin.ui.TextArea; | |||
import com.vaadin.ui.TextField; | |||
public class TextChangeListenerLosesFocus extends TestBase { | |||
private final TextChangeListener listener = new TextChangeListener() { | |||
public void textChange(TextChangeEvent event) { | |||
final String value = event.getText(); | |||
if (value.length() > 2) { | |||
((Field) event.getComponent()) | |||
.setValue("Updated by TextChangeListener"); | |||
} | |||
} | |||
}; | |||
@Override | |||
protected void setup() { | |||
TestUtils.injectCSS(getMainWindow(), | |||
".v-textfield-focus, .v-textarea-focus { " | |||
+ " background: #E8F0FF !important }"); | |||
AbstractTextField field = new TextField(); | |||
field.setDebugId("test-textfield"); | |||
field.setInputPrompt("Enter at least 3 characters"); | |||
field.addListener(listener); | |||
addComponent(field); | |||
field = new TextArea(); | |||
field.setDebugId("test-textarea"); | |||
field.setInputPrompt("Enter at least 3 characters"); | |||
field.addListener(listener); | |||
addComponent(field); | |||
} | |||
@Override | |||
protected String getDescription() { | |||
return "Updating a focused TextField overwrites the focus stylename"; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 11623; | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> | |||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> | |||
<head profile="http://selenium-ide.openqa.org/profiles/test-case"> | |||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |||
<link rel="selenium.base" href="http://localhost:8070/" /> | |||
<title>MarginWithExpandRatio</title> | |||
</head> | |||
<body> | |||
<table cellpadding="1" cellspacing="1" border="1"> | |||
<thead> | |||
<tr><td rowspan="1" colspan="3">MarginWithExpandRatio</td></tr> | |||
</thead><tbody> | |||
<tr> | |||
<td>open</td> | |||
<td>/run/MarginWithExpandRatio?restartApplication</td> | |||
<td></td> | |||
</tr> | |||
<tr> | |||
<td>screenCapture</td> | |||
<td></td> | |||
<td>no-overflow</td> | |||
</tr> | |||
</tbody></table> | |||
</body> | |||
</html> |
@@ -0,0 +1,85 @@ | |||
/* | |||
* Copyright 2000-2013 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.layouts; | |||
import com.vaadin.server.VaadinRequest; | |||
import com.vaadin.tests.components.AbstractTestUI; | |||
import com.vaadin.tests.util.TestUtils; | |||
import com.vaadin.ui.GridLayout; | |||
import com.vaadin.ui.HorizontalLayout; | |||
import com.vaadin.ui.Label; | |||
import com.vaadin.ui.Layout; | |||
import com.vaadin.ui.Panel; | |||
import com.vaadin.ui.VerticalLayout; | |||
public class MarginWithExpandRatio extends AbstractTestUI { | |||
@Override | |||
protected void setup(VaadinRequest request) { | |||
TestUtils.injectCSS(this, | |||
".hugemargin { margin: 10px 20px !important; }"); | |||
HorizontalLayout hl = new HorizontalLayout(); | |||
addLayoutTest(hl); | |||
hl.setExpandRatio(hl.getComponent(0), 1.0f); | |||
hl.setExpandRatio(hl.getComponent(2), 0.5f); | |||
VerticalLayout vl = new VerticalLayout(); | |||
addLayoutTest(vl); | |||
vl.setExpandRatio(vl.getComponent(0), 1.0f); | |||
vl.setExpandRatio(vl.getComponent(2), 0.5f); | |||
GridLayout gl = new GridLayout(2, 1); | |||
addLayoutTest(gl); | |||
gl.setColumnExpandRatio(0, 1.0f); | |||
gl.setRowExpandRatio(0, 1.0f); | |||
gl.setColumnExpandRatio(1, 0.5f); | |||
gl.setRowExpandRatio(1, 0.5f); | |||
} | |||
@Override | |||
protected String getTestDescription() { | |||
return "Layout content overflows if CSS margin used with expand ratio"; | |||
} | |||
@Override | |||
protected Integer getTicketNumber() { | |||
return 11553; | |||
} | |||
private void addLayoutTest(Layout l) { | |||
l.setSizeFull(); | |||
Label lbl = new Label("First (expand ratio 1)"); | |||
lbl.setSizeUndefined(); | |||
l.addComponent(lbl); | |||
lbl = new Label("Second (margin 10px)"); | |||
lbl.setSizeUndefined(); | |||
lbl.addStyleName("hugemargin"); | |||
l.addComponent(lbl); | |||
lbl = new Label("Third (margin+xr)"); | |||
lbl.setSizeUndefined(); | |||
lbl.addStyleName("hugemargin"); | |||
l.addComponent(lbl); | |||
Panel p = new Panel(l.getClass().getSimpleName(), l); | |||
p.setWidth("600px"); | |||
p.setHeight("200px"); | |||
addComponent(p); | |||
} | |||
} |
@@ -182,6 +182,7 @@ | |||
<sequential> | |||
<ant antfile="${test.xml.dir}/vaadin-server.xml" target="wait-for-startup" /> | |||
<antcall inheritall="true" inheritrefs="true" target="run-and-clean-up" /> | |||
<echo message="All TestBench tests have been run" /> | |||
</sequential> | |||
</parallel> | |||
</target> |
@@ -8,7 +8,7 @@ | |||
<ivy:resolve file="${dir}/ivy.xml" /> | |||
<ivy:cachepath pathid="classpath.jetty" conf="jetty-run" /> | |||
<java classname="org.mortbay.jetty.runner.Runner" fork="yes"> | |||
<java classname="org.mortbay.jetty.runner.Runner" fork="yes" output="${vaadin.basedir}/result/jetty.java.out" resultproperty="resultCode"> | |||
<arg value="--port" /> | |||
<arg value="8888" /> | |||
<arg value="--out" /> | |||
@@ -19,6 +19,7 @@ | |||
<classpath refid="classpath.jetty" /> | |||
<jvmarg value="-ea" /> | |||
</java> | |||
<echo message="Jetty process ended with result code ${resultCode}" /> | |||
</target> | |||