Author: gseitz Date: Fri May 9 16:44:20 2008 New Revision: 654981 URL: http://svn.apache.org/viewvc?rev=654981&view=rev Log: WICKET-1608: make AutoCompleteBehavior's configuration more flexible WICKET-1595: provide a scrolling mechanism that's actually usable WICKET-1562: Autocomplete should display the selection list even if the input field is empty
Added: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java (with props) Modified: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteBehavior.java wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteTextField.java wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js Modified: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java URL: http://svn.apache.org/viewvc/wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java?rev=654981&r1=654980&r2=654981&view=diff ============================================================================== --- wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java (original) +++ wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java Fri May 9 16:44:20 2008 @@ -16,6 +16,7 @@ */ package org.apache.wicket.extensions.ajax.markup.html.autocomplete; + import org.apache.wicket.RequestCycle; import org.apache.wicket.ResourceReference; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; @@ -37,21 +38,47 @@ protected boolean preselect = false; + protected AutoCompleteSettings settings = new AutoCompleteSettings(); + /** * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#renderHead(org.apache.wicket.markup.html.IHeaderResponse) */ + public void renderHead(IHeaderResponse response) { super.renderHead(response); response.renderJavascriptReference(AUTOCOMPLETE_JS); final String id = getComponent().getMarkupId(); - response.renderOnDomReadyJavascript("new Wicket.AutoComplete('" + id + "','" + - getCallbackUrl() + "'," + preselect + ");"); + String initJS = constructInitJS(); + response.renderOnDomReadyJavascript(initJS); + } + + protected final String constructInitJS() + { + StringBuffer sb = new StringBuffer(); + sb.append("new Wicket.AutoComplete('") + .append(getComponent().getMarkupId()) + .append("','") + .append(getCallbackUrl()) + .append("',") + .append(constructSettingsJS()) + .append(");"); + return sb.toString(); + } + + protected final String constructSettingsJS() + { + StringBuffer sb = new StringBuffer(); + sb.append("{preselect:").append(settings.getPreselect()).append(",maxHeight:").append( + settings.getMaxHeightInPx()).append(",showListOnEmptyInput:").append( + settings.getShowListOnEmptyInput()).append("}"); + return sb.toString(); } /** * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#onBind() */ + protected void onBind() { // add empty AbstractDefaultAjaxBehavior to the component, to force @@ -61,6 +88,7 @@ { private static final long serialVersionUID = 1L; + protected void respond(AjaxRequestTarget target) { } @@ -81,6 +109,7 @@ /** * @see org.apache.wicket.ajax.AbstractDefaultAjaxBehavior#respond(org.apache.wicket.ajax.AjaxRequestTarget) */ + protected void respond(AjaxRequestTarget target) { final RequestCycle requestCycle = RequestCycle.get(); Modified: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteBehavior.java URL: http://svn.apache.org/viewvc/wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteBehavior.java?rev=654981&r1=654980&r2=654981&view=diff ============================================================================== --- wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteBehavior.java (original) +++ wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteBehavior.java Fri May 9 16:44:20 2008 @@ -28,6 +28,8 @@ * This behavior builds on top of [EMAIL PROTECTED] AbstractAutoCompleteBehavior} by introducing the concept of * a [EMAIL PROTECTED] IAutoCompleteRenderer} to make response writing easier. * + * @param + * * @see IAutoCompleteRenderer * * @since 1.2 @@ -63,23 +65,37 @@ */ public AutoCompleteBehavior(IAutoCompleteRenderer renderer, boolean preselect) { + this(renderer, new AutoCompleteSettings().setPreselect(preselect)); + } + + /** + * Constructor + * + * @param renderer + * renderer that will be used to generate output + * @param settings + * settings for the autocomplete list + */ + public AutoCompleteBehavior(IAutoCompleteRenderer renderer, AutoCompleteSettings settings) + { if (renderer == null) { throw new IllegalArgumentException("renderer cannot be null"); } + if (settings == null) + { + settings = new AutoCompleteSettings(); + } this.renderer = renderer; - this.preselect = preselect; + this.settings = settings; } - protected final void onRequest(final String val, RequestCycle requestCycle) { IRequestTarget target = new IRequestTarget() { - public void respond(RequestCycle requestCycle) { - WebResponse r = (WebResponse)requestCycle.getResponse(); // Determine encoding Added: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java URL: http://svn.apache.org/viewvc/wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java?rev=654981&view=auto ============================================================================== --- wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java (added) +++ wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java Fri May 9 16:44:20 2008 @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.wicket.extensions.ajax.markup.html.autocomplete; + +import org.apache.wicket.IClusterable; + +/** + * This class encapsulates various settings for [EMAIL PROTECTED] AbstractAutoCompleteBehavior}. See the + * documentation for the property accessors of this class for further information. + * <p> + * Default settings: <table> + * <tr> + * <th>setting</th> + * <th>default value</th> + * </tr> + * <tr> + * <td>preselect</td> + * <td>false</td> + * </tr> + * <tr> + * <td>maxHeightInPx</td> + * <td>-1</td> + * </tr> + * <tr> + * <td>showListOnEmptyInput</td> + * <td>false</td> + * </tr> + * </table> + * </p> + * + * @author Gerolf Seitz + */ +public final class AutoCompleteSettings implements IClusterable +{ + private static final long serialVersionUID = 1L; + + private boolean preselect = false; + + private int maxHeightInPx = -1; + + private boolean showListOnEmptyInput = false; + + /** + * Indicates whether the first item in the list is automatically selected when the autocomplete + * list is shown. + * + * @return true if the first item of the autocomplete list should be preselected, false + * (default) otherwise + */ + public boolean getPreselect() + { + return preselect; + } + + /** + * Sets whether the first item in the autocomplete list should be selected when the autocomplete + * list is shown. + * + * @param preselect + * the flag + * @return this [EMAIL PROTECTED] AutoCompleteSettings} + */ + public AutoCompleteSettings setPreselect(boolean preselect) + { + this.preselect = preselect; + return this; + } + + /** + * Gets the maximum height of the autocomplete list in pixels. <code>-1</code> indicates that + * the autocomplete list should have no maximum height. + * + * @return the maximum height in pixels + */ + public int getMaxHeightInPx() + { + return maxHeightInPx; + } + + /** + * Sets the maximum height in pixels of the autocomplete list. + * <p> + * The maximum height can also be specified via css (and by setting maxHeightInPx to -1): + * + * <pre> + * div.wicket-aa-container { maxHeight: 100px; } + * </pre> + * + * Note that this does not work in IE6. + * </p> + * + * @param maxHeightInPx + * the maximum height in pixels + * @return this [EMAIL PROTECTED] AutoCompleteSettings} + */ + public AutoCompleteSettings setMaxHeightInPx(int maxHeightInPx) + { + this.maxHeightInPx = maxHeightInPx; + return this; + } + + /** + * Indicates whether the autocomplete list will be shown if the input is empty. + * + * @return true if the autocomlete list will be shown if the input string is empty, false + * otherwise + */ + public boolean getShowListOnEmptyInput() + { + return showListOnEmptyInput; + } + + /** + * Sets whether the list should be shown when the input is empty. + * + * @param showListOnEmptyInput + * the flag + * @return this [EMAIL PROTECTED] AutoCompleteSettings} + */ + public AutoCompleteSettings setShowListOnEmptyInput(boolean showListOnEmptyInput) + { + this.showListOnEmptyInput = showListOnEmptyInput; + return this; + } +} \ No newline at end of file Propchange: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteTextField.java URL: http://svn.apache.org/viewvc/wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteTextField.java?rev=654981&r1=654980&r2=654981&view=diff ============================================================================== --- wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteTextField.java (original) +++ wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteTextField.java Fri May 9 16:44:20 2008 @@ -32,6 +32,9 @@ * @since 1.2 * * @author Igor Vaynberg (ivaynberg) + * + * @param The + * model object type */ public abstract class AutoCompleteTextField extends TextField { @@ -57,8 +60,20 @@ public AutoCompleteTextField(String id, IModel model, Class type, boolean preselect) { this(id, model, type, StringAutoCompleteRenderer.INSTANCE, preselect); + } - } + /** + * Construct. + * + * @param id + * @param model + * @param type + * @param settings + */ + public AutoCompleteTextField(String id, IModel model, Class type, AutoCompleteSettings settings) + { + this(id, model, type, StringAutoCompleteRenderer.INSTANCE, settings); + } /** * @param id @@ -70,6 +85,18 @@ this(id, object, (Class)null, preselect); } + /** + * Construct. + * + * @param id + * @param object + * @param settings + */ + public AutoCompleteTextField(String id, IModel object, AutoCompleteSettings settings) + { + this(id, object, (Class)null, settings); + } + /** * @param id @@ -87,6 +114,17 @@ public AutoCompleteTextField(String id, boolean preselect) { this(id, (IModel)null, preselect); + } + + /** + * Construct. + * + * @param id + * @param settings + */ + public AutoCompleteTextField(String id, AutoCompleteSettings settings) + { + this(id, (IModel)null, settings); } @@ -138,12 +176,26 @@ public AutoCompleteTextField(String id, IModel model, Class type, IAutoCompleteRenderer renderer, boolean preselect) { - super(id, model, type); + this(id, model, type, renderer, new AutoCompleteSettings().setPreselect(preselect)); + } + /** + * Construct. + * + * @param id + * @param model + * @param type + * @param renderer + * @param settings + */ + public AutoCompleteTextField(String id, IModel model, Class type, + IAutoCompleteRenderer renderer, AutoCompleteSettings settings) + { + super(id, model, type); // this disables Firefox autocomplete add(new SimpleAttributeModifier("autocomplete", "off")); - add(new AutoCompleteBehavior(renderer, preselect) + add(new AutoCompleteBehavior(renderer, settings) { private static final long serialVersionUID = 1L; @@ -154,7 +206,6 @@ } }); - } /** @@ -170,5 +221,10 @@ */ protected abstract Iterator getChoices(String input); + protected int getMaxHeightInPx() + { + return 50; + } + } Modified: wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js URL: http://svn.apache.org/viewvc/wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js?rev=654981&r1=654980&r2=654981&view=diff ============================================================================== --- wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js (original) +++ wicket/branches/wicket-1.3.x/jdk-1.4/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js Fri May 9 16:44:20 2008 @@ -27,7 +27,7 @@ enterHidesWithNoSelection : false }; -Wicket.AutoComplete=function(elementId, callbackUrl, preselect){ +Wicket.AutoComplete=function(elementId, callbackUrl, cfg){ var KEY_TAB=9; var KEY_ENTER=13; var KEY_ESC=27; @@ -87,7 +87,7 @@ } else { render(); } - if(navigator.appVersion.indexOf('AppleWebKit')>0)return killEvent(event); + if(Wicket.Browser.isSafari())return killEvent(event); break; case KEY_DOWN: if(selected<elementCount-1){ @@ -99,7 +99,7 @@ render(); showAutoComplete(); } - if(navigator.appVersion.indexOf('AppleWebKit')>0)return killEvent(event); + if(Wicket.Browser.isSafari())return killEvent(event); break; case KEY_ESC: hideAutoComplete(); @@ -166,24 +166,38 @@ function getAutocompleteMenu() { var choiceDiv=document.getElementById(getMenuId()); if (choiceDiv==null) { + var container = document.createElement("div"); + container.className="wicket-aa-container"; + document.body.appendChild(container); + container.style.display="none"; + container.style.overflow="auto"; + container.style.position="absolute"; + container.id=getMenuId()+"-container"; + + container.show = function() { wicketShow(this.id) }; + container.hide = function() { wicketHide(this.id) }; + choiceDiv=document.createElement("div"); - document.body.appendChild(choiceDiv); + container.appendChild(choiceDiv); choiceDiv.id=getMenuId(); choiceDiv.className="wicket-aa"; - choiceDiv.style.display="none"; - choiceDiv.style.position="absolute"; + // WICKET-1350/WICKET-1351 - choiceDiv.onmouseout=function() {mouseactive=0;}; - choiceDiv.onmouseover=function() {mouseactive=1;}; + container.onmouseout=function() {mouseactive=0;}; + container.onmousemove=function() {mouseactive=1;}; } - choiceDiv.show = function() { wicketShow(this.id) } - choiceDiv.hide = function() { wicketHide(this.id) } return choiceDiv; } + function getAutocompleteContainer() { + var node=getAutocompleteMenu().parentNode; + + return node; + } + function killEvent(event){ if(!event)event=window.event; if(!event)return false; @@ -203,7 +217,7 @@ } function updateChoices(){ - if(preselect==true){ + if(cfg.preselect==true){ selected = 0; } else{ @@ -220,14 +234,14 @@ function showAutoComplete(){ var position=getPosition(wicketGet(elementId)); - var menu = getAutocompleteMenu(); + var container = getAutocompleteContainer(); var input=wicketGet(elementId); - var index=getOffsetParentZIndex(elementId); - menu.show(); - menu.style.zIndex=(Number(index)!=Number.NaN?Number(index)+1:index); - menu.style.left=position[0]+'px' - menu.style.top=(input.offsetHeight+position[1])+'px'; - menu.style.width=input.offsetWidth+'px'; + var index=getOffsetParentZIndex(elementId); + container.show(); + container.style.zIndex=(Number(index)!=Number.NaN?Number(index)+1:index); + container.style.left=position[0]+'px' + container.style.top=(input.offsetHeight+position[1])+'px'; + container.style.width=input.offsetWidth+'px'; visible=1; hideShowCovered(); } @@ -235,9 +249,9 @@ function hideAutoComplete(){ visible=0; selected=-1; - if ( document.getElementById(getMenuId()) ) + if ( getAutocompleteContainer() ) { - getAutocompleteMenu().hide(); + getAutocompleteContainer().hide(); hideShowCovered(); } } @@ -257,7 +271,7 @@ // check if the input hasn't been cleared in the meanwhile var input=wicketGet(elementId); - if (input.value==null || input.value=="") { + if (!cfg.showListOnEmptyInput && (input.value==null || input.value=="")) { hideAutoComplete(); return; } @@ -303,7 +317,7 @@ function scheduleEmptyCheck() { window.setTimeout(function() { var input=wicketGet(elementId); - if (input.value==null || input.value=="") { + if (!cfg.showListOnEmptyInput && (input.value==null || input.value=="")) { hideAutoComplete(); } }, 100); @@ -332,11 +346,22 @@ function stripHTML(str) { return str.replace(/<[^>]+>/g,""); } + + function adjustScrollOffset(menu, item) { + if (item.offsetTop + item.offsetHeight > menu.scrollTop + menu.offsetHeight) { + menu.scrollTop = item.offsetTop + item.offsetHeight - menu.offsetHeight; + } else + // adjust to the top + if (item.offsetTop < menu.scrollTop) { + menu.scrollTop = item.offsetTop; + } + } function render(){ - var element= getAutocompleteMenu(); + var menu=getAutocompleteMenu(); + var height=0; for(var i=0;i<elementCount;i++){ - var node=element.firstChild.childNodes[i]; + var node=menu.firstChild.childNodes[i]; var classNames=node.className.split(" "); for (var j=0; j<classNames.length; j++) { @@ -347,13 +372,18 @@ if(selected==i){ classNames.push('selected'); - node.scrollIntoView(true); + adjustScrollOffset(menu.parentNode, node); } - + node.className=classNames.join(" "); + height+=node.offsetHeight; + } + if (cfg.maxHeight != -1) { + height = height<cfg.maxHeight?height:cfg.maxHeight; + menu.parentNode.style.height=height+"px"; } } - + // From http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/ function getStyle(obj,cssRule) { var cssRuleAlt = cssRule.replace(/\-(\w)/g,function(strMatch,p1){return p1.toUpperCase();}); @@ -398,7 +428,7 @@ } function hideShowCoveredTimeout(){ - var el=getAutocompleteMenu(); + var el=getAutocompleteContainer(); var p=getPosition(el); var acLeftX=p[0];