From e03ac3fe1c5934cf24ed0aea58226baf43efb159 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Wed, 8 Aug 2012 14:56:05 +0000 Subject: [PATCH] Fixed TextArea maxlength so it is always enforced (#9199) Uses native maxlength attribute for browsers which support it. svn changeset:24123/svn branch:6.8 --- .../terminal/gwt/client/BrowserInfo.java | 78 ++++++++++++---- .../terminal/gwt/client/ui/VTextArea.java | 91 +++++++++++++++---- .../terminal/gwt/client/ui/VTextField.java | 32 ++++--- .../textarea/TextAreaMaxLength.html | 53 +++++++++++ 4 files changed, 210 insertions(+), 44 deletions(-) create mode 100755 tests/testbench/com/vaadin/tests/components/textarea/TextAreaMaxLength.html diff --git a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java index 4b9f3eacab..6a94611a60 100644 --- a/src/com/vaadin/terminal/gwt/client/BrowserInfo.java +++ b/src/com/vaadin/terminal/gwt/client/BrowserInfo.java @@ -128,7 +128,7 @@ public class BrowserInfo { if (browserDetails.isFirefox()) { browserIdentifier = BROWSER_FIREFOX; majorVersionClass = browserIdentifier - + browserDetails.getBrowserMajorVersion(); + + getBrowserMajorVersion(); minorVersionClass = majorVersionClass + browserDetails.getBrowserMinorVersion(); browserEngineClass = ENGINE_GECKO; @@ -140,21 +140,21 @@ public class BrowserInfo { } else if (browserDetails.isSafari()) { browserIdentifier = BROWSER_SAFARI; majorVersionClass = browserIdentifier - + browserDetails.getBrowserMajorVersion(); + + getBrowserMajorVersion(); minorVersionClass = majorVersionClass + browserDetails.getBrowserMinorVersion(); browserEngineClass = ENGINE_WEBKIT; } else if (browserDetails.isIE()) { browserIdentifier = BROWSER_IE; majorVersionClass = browserIdentifier - + browserDetails.getBrowserMajorVersion(); + + getBrowserMajorVersion(); minorVersionClass = majorVersionClass + browserDetails.getBrowserMinorVersion(); browserEngineClass = ENGINE_TRIDENT; } else if (browserDetails.isOpera()) { browserIdentifier = BROWSER_OPERA; majorVersionClass = browserIdentifier - + browserDetails.getBrowserMajorVersion(); + + getBrowserMajorVersion(); minorVersionClass = majorVersionClass + browserDetails.getBrowserMinorVersion(); browserEngineClass = ENGINE_PRESTO; @@ -211,23 +211,23 @@ public class BrowserInfo { } public boolean isSafari4() { - return isSafari() && browserDetails.getBrowserMajorVersion() == 4; + return isSafari() && getBrowserMajorVersion() == 4; } public boolean isIE6() { - return isIE() && browserDetails.getBrowserMajorVersion() == 6; + return isIE() && getBrowserMajorVersion() == 6; } public boolean isIE7() { - return isIE() && browserDetails.getBrowserMajorVersion() == 7; + return isIE() && getBrowserMajorVersion() == 7; } public boolean isIE8() { - return isIE() && browserDetails.getBrowserMajorVersion() == 8; + return isIE() && getBrowserMajorVersion() == 8; } public boolean isIE9() { - return isIE() && browserDetails.getBrowserMajorVersion() == 9; + return isIE() && getBrowserMajorVersion() == 9; } public boolean isChrome() { @@ -255,8 +255,7 @@ public class BrowserInfo { } public boolean isFF4() { - return browserDetails.isFirefox() - && browserDetails.getBrowserMajorVersion() == 4; + return browserDetails.isFirefox() && getBrowserMajorVersion() == 4; } /** @@ -292,7 +291,7 @@ public class BrowserInfo { return -1; } - return browserDetails.getBrowserMajorVersion(); + return getBrowserMajorVersion(); } public float getOperaVersion() { @@ -300,7 +299,7 @@ public class BrowserInfo { return -1; } - return browserDetails.getBrowserMajorVersion(); + return getBrowserMajorVersion(); } public boolean isOpera() { @@ -308,13 +307,11 @@ public class BrowserInfo { } public boolean isOpera10() { - return browserDetails.isOpera() - && browserDetails.getBrowserMajorVersion() == 10; + return browserDetails.isOpera() && getBrowserMajorVersion() == 10; } public boolean isOpera11() { - return browserDetails.isOpera() - && browserDetails.getBrowserMajorVersion() == 11; + return browserDetails.isOpera() && getBrowserMajorVersion() == 11; } public native static String getBrowserString() @@ -475,4 +472,51 @@ public class BrowserInfo { private int getOperatingSystemMajorVersion() { return browserDetails.getOperatingSystemMajorVersion(); } + + /** + * Returns the browser major version e.g., 3 for Firefox 3.5, 4 for Chrome + * 4, 8 for Internet Explorer 8. + *

+ * Note that Internet Explorer 8 and newer will return the document mode so + * IE8 rendering as IE7 will return 7. + *

+ * + * @return The major version of the browser. + */ + public int getBrowserMajorVersion() { + return browserDetails.getBrowserMajorVersion(); + } + + /** + * Returns the browser minor version e.g., 5 for Firefox 3.5. + * + * @see #getBrowserMajorVersion() + * + * @return The minor version of the browser, or -1 if not known/parsed. + */ + public int getBrowserMinorVersion() { + return browserDetails.getBrowserMinorVersion(); + } + + /** + * Checks if the browser version is newer or equal to the given major+minor + * version. + * + * @param majorVersion + * The major version to check for + * @param minorVersion + * The minor version to check for + * @return true if the browser version is newer or equal to the given + * version + */ + public boolean isBrowserVersionNewerOrEqual(int majorVersion, + int minorVersion) { + if (getBrowserMajorVersion() == majorVersion) { + // Same major + return (getBrowserMinorVersion() >= minorVersion); + } + + // Older or newer major + return (getBrowserMajorVersion() > majorVersion); + } } diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java b/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java index 0ed5887442..4ab27646ed 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextArea.java @@ -5,12 +5,17 @@ package com.vaadin.terminal.gwt.client.ui; import com.google.gwt.core.client.Scheduler; +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.vaadin.terminal.gwt.client.ApplicationConnection; +import com.vaadin.terminal.gwt.client.BrowserInfo; import com.vaadin.terminal.gwt.client.UIDL; /** @@ -24,10 +29,17 @@ import com.vaadin.terminal.gwt.client.UIDL; */ public class VTextArea extends VTextField { public static final String CLASSNAME = "v-textarea"; + private MaxLengthHandler maxLengthHandler = new MaxLengthHandler(); + private boolean browserSupportsMaxLengthAttribute = browserSupportsMaxLengthAttribute(); public VTextArea() { super(DOM.createTextArea()); setStyleName(CLASSNAME); + if (!browserSupportsMaxLengthAttribute) { + addKeyUpHandler(maxLengthHandler); + addChangeHandler(maxLengthHandler); + sinkEvents(Event.ONPASTE); + } } @Override @@ -39,8 +51,69 @@ public class VTextArea extends VTextField { setRows(uidl.getIntAttribute("rows")); } - if (getMaxLength() >= 0) { - sinkEvents(Event.ONKEYUP); + } + + private class MaxLengthHandler implements KeyUpHandler, ChangeHandler { + + public void onKeyUp(KeyUpEvent event) { + enforceMaxLength(); + } + + public void onPaste(Event event) { + enforceMaxLength(); + } + + private void enforceMaxLength() { + if (getMaxLength() >= 0) { + Scheduler.get().scheduleDeferred(new Command() { + public void execute() { + if (getText().length() > getMaxLength()) { + setText(getText().substring(0, getMaxLength())); + } + } + }); + } + } + + public void onChange(ChangeEvent event) { + // Opera does not support paste events so this enforces max length + // for Opera. + enforceMaxLength(); + } + + } + + protected boolean browserSupportsMaxLengthAttribute() { + BrowserInfo info = BrowserInfo.get(); + if (info.isFirefox() && info.isBrowserVersionNewerOrEqual(4, 0)) { + return true; + } + if (info.isSafari() && info.isBrowserVersionNewerOrEqual(5, 0)) { + return true; + } + if (info.isIE() && info.isBrowserVersionNewerOrEqual(10, 0)) { + return true; + } + if (info.isAndroid() && info.isBrowserVersionNewerOrEqual(2, 3)) { + return true; + } + return false; + } + + @Override + protected void updateMaxLength(int maxLength) { + if (browserSupportsMaxLengthAttribute) { + super.updateMaxLength(maxLength); + } else { + // Handled automatically by MaxLengthHandler + } + } + + @Override + public void onBrowserEvent(Event event) { + super.onBrowserEvent(event); + if (event.getTypeInt() == Event.ONPASTE) { + maxLengthHandler.onPaste(event); } } @@ -56,20 +129,6 @@ public class VTextArea extends VTextField { } catch (e) {} }-*/; - @Override - public void onBrowserEvent(Event event) { - if (getMaxLength() >= 0 && event.getTypeInt() == Event.ONKEYUP) { - Scheduler.get().scheduleDeferred(new Command() { - public void execute() { - if (getText().length() > getMaxLength()) { - setText(getText().substring(0, getMaxLength())); - } - } - }); - } - super.onBrowserEvent(event); - } - @Override public int getCursorPos() { // This is needed so that TextBoxImplIE6 is used to return the correct diff --git a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java index b05b9ba830..b24e304b82 100644 --- a/src/com/vaadin/terminal/gwt/client/ui/VTextField.java +++ b/src/com/vaadin/terminal/gwt/client/ui/VTextField.java @@ -385,20 +385,30 @@ public class VTextField extends TextBoxBase implements Paintable, Field, } private void setMaxLength(int newMaxLength) { - if (newMaxLength >= 0) { + if (newMaxLength >= 0 && newMaxLength != maxLength) { maxLength = newMaxLength; - if (getElement().getTagName().toLowerCase().equals("textarea")) { - // NOP no maxlength property for textarea - } else { - getElement().setPropertyInt("maxLength", maxLength); - } + updateMaxLength(maxLength); } else if (maxLength != -1) { - if (getElement().getTagName().toLowerCase().equals("textarea")) { - // NOP no maxlength property for textarea - } else { - getElement().removeAttribute("maxLength"); - } maxLength = -1; + updateMaxLength(maxLength); + } + + } + + /** + * This method is reponsible for updating the DOM or otherwise ensuring that + * the given max length is enforced. Called when the max length for the + * field has changed. + * + * @param maxLength + * The new max length + */ + protected void updateMaxLength(int maxLength) { + if (maxLength >= 0) { + getElement().setPropertyInt("maxLength", maxLength); + } else { + getElement().removeAttribute("maxLength"); + } } diff --git a/tests/testbench/com/vaadin/tests/components/textarea/TextAreaMaxLength.html b/tests/testbench/com/vaadin/tests/components/textarea/TextAreaMaxLength.html new file mode 100755 index 0000000000..5fe43b7b7a --- /dev/null +++ b/tests/testbench/com/vaadin/tests/components/textarea/TextAreaMaxLength.html @@ -0,0 +1,53 @@ + + + + + + +New Test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
New Test
open/run/com.vaadin.tests.components.textarea.TextAreaTest?debug&restartApplication
mouseClickvaadin=runcomvaadintestscomponentstextareaTextAreaTest::PID_Smenu#item020,9
mouseClickvaadin=runcomvaadintestscomponentstextareaTextAreaTest::Root/VOverlay[0]/VMenuBar[0]#item543,8
mouseClickvaadin=runcomvaadintestscomponentstextareaTextAreaTest::Root/VOverlay[1]/VMenuBar[0]#item270,13
mouseClickvaadin=runcomvaadintestscomponentstextareaTextAreaTest::Root/VOverlay[2]/VMenuBar[0]#item540,9
enterCharactervaadin=runcomvaadintestscomponentstextareaTextAreaTest::PID_StestComponent0123456789
assertValuevaadin=runcomvaadintestscomponentstextareaTextAreaTest::PID_StestComponent01234
+ + -- 2.39.5