You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by jc...@apache.org on 2009/12/03 11:50:02 UTC
svn commit: r886735 -
/wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/
Author: jcompagner
Date: Thu Dec 3 10:50:01 2009
New Revision: 886735
URL: http://svn.apache.org/viewvc?rev=886735&view=rev
Log:
fixes and improvements (browser issues)
Modified:
wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java
wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java
wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/DefaultCssAutocompleteTextField.css
wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js
Modified: wicket/branches/wicket-1.4.x/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.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java?rev=886735&r1=886734&r2=886735&view=diff
==============================================================================
--- wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java (original)
+++ wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AbstractAutoCompleteBehavior.java Thu Dec 3 10:50:01 2009
@@ -82,6 +82,8 @@
sb.append("{preselect: ").append(settings.getPreselect());
sb.append(",maxHeight: ").append(settings.getMaxHeightInPx());
sb.append(",adjustInputWidth: ").append(settings.isAdjustInputWidth());
+ sb.append(",useSmartPositioning: ").append(settings.getUseSmartPositioning());
+ sb.append(",useHideShowCoveredIEFix: ").append(settings.getUseHideShowCoveredIEFix());
sb.append(",showListOnEmptyInput: ").append(settings.getShowListOnEmptyInput());
sb.append(",showListOnFocusGain: ").append(settings.getShowListOnFocusGain());
sb.append(",throttleDelay: ").append(settings.getThrottleDelay());
Modified: wicket/branches/wicket-1.4.x/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.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java?rev=886735&r1=886734&r2=886735&view=diff
==============================================================================
--- wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java (original)
+++ wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/AutoCompleteSettings.java Thu Dec 3 10:50:01 2009
@@ -55,6 +55,10 @@
private boolean showListOnEmptyInput = false;
+ private boolean useSmartPositioning = false;
+
+ private boolean useHideShowCoveredIEFix = true;
+
private String cssClassName = null;
private boolean adjustInputWidth = true;
@@ -148,6 +152,29 @@
}
/**
+ * Indicates whether the popup positioning will take into account browser window visible area or
+ * not. (so always show popup bottom-right or not)
+ *
+ * @return true if popup smart positioning is used, false otherwise.
+ */
+ public boolean getUseSmartPositioning()
+ {
+ return useSmartPositioning;
+ }
+
+ /**
+ * Indicates whether in case of IE (and Opera), "select" "iframe" and "applet" tags should be
+ * hidden if covered by popup. (as they might appear on top)<br>
+ * By default this is true (before this flag was added).
+ *
+ * @return true if the fix/workaround should be used for IE and Opera, false otherwise.
+ */
+ public boolean getUseHideShowCoveredIEFix()
+ {
+ return useHideShowCoveredIEFix;
+ }
+
+ /**
* 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
@@ -189,10 +216,12 @@
*
* @param cssClassName
* valid CSS class name
+ * @return this {@link AutoCompleteSettings}.
*/
- public void setCssClassName(final String cssClassName)
+ public AutoCompleteSettings setCssClassName(final String cssClassName)
{
this.cssClassName = cssClassName;
+ return this;
}
/**
@@ -216,10 +245,12 @@
* @param adjustInputWidth
* <code>true</code> if the autocompleter should have the same size as the input
* field, <code>false</code> for default browser behavior
+ * @return this {@link AutoCompleteSettings}.
*/
- public void setAdjustInputWidth(final boolean adjustInputWidth)
+ public AutoCompleteSettings setAdjustInputWidth(final boolean adjustInputWidth)
{
this.adjustInputWidth = adjustInputWidth;
+ return this;
}
/**
@@ -238,10 +269,12 @@
*
* @param showListOnEmptyInput
* the flag
+ * @return this {@link AutoCompleteSettings}.
*/
- public void setShowCompleteListOnFocusGain(final boolean showCompleteListOnFocusGain)
+ public AutoCompleteSettings setShowCompleteListOnFocusGain(final boolean showCompleteListOnFocusGain)
{
this.showCompleteListOnFocusGain = showCompleteListOnFocusGain;
+ return this;
}
/**
@@ -260,9 +293,41 @@
*
* @param showListOnEmptyInput
* the flag
+ * @return this {@link AutoCompleteSettings}.
*/
- public void setShowListOnFocusGain(final boolean showListOnFocusGain)
+ public AutoCompleteSettings setShowListOnFocusGain(final boolean showListOnFocusGain)
{
this.showListOnFocusGain = showListOnFocusGain;
+ return this;
}
+
+ /**
+ * Sets whether the popup positioning will take into account browser window visible area or not. (so always show popup bottom-right or not)<br>
+ * THIS WILL PRODUCE UNWANTED BEHAVIOR WITH IE versions < 8 (probably because of unreliable clientWidth/clientHeight browser element properties).
+ *
+ * @param useSmartPositioning
+ * the flag
+ * @return this {@link AutoCompleteSettings}.
+ */
+ public AutoCompleteSettings setUseSmartPositioning(final boolean useSmartPositioning)
+ {
+ this.useSmartPositioning = useSmartPositioning;
+ return this;
+ }
+
+ /**
+ * Indicates whether in case of IE (and Opera), "select" "iframe" and "applet" tags should be
+ * hidden if covered by popup. (as they might appear on top)<br>
+ * By default this is true (before this flag was added).
+ *
+ * @param useHideShowCoveredIEFix
+ * the flag
+ * @return this {@link AutoCompleteSettings}.
+ */
+ public AutoCompleteSettings setUseHideShowCoveredIEFix(final boolean useHideShowCoveredIEFix)
+ {
+ this.useHideShowCoveredIEFix = useHideShowCoveredIEFix;
+ return this;
+ }
+
}
Modified: wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/DefaultCssAutocompleteTextField.css
URL: http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/DefaultCssAutocompleteTextField.css?rev=886735&r1=886734&r2=886735&view=diff
==============================================================================
--- wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/DefaultCssAutocompleteTextField.css (original)
+++ wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/DefaultCssAutocompleteTextField.css Thu Dec 3 10:50:01 2009
@@ -14,18 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+div.wicket-aa-container {
+ border-width: 1px;
+ border-color: #cccccc;
+ border-style: solid;
+}
+
div.wicket-aa {
font-family: "Lucida Grande","Lucida Sans Unicode",Tahoma,Verdana;
font-size: 12px;
background-color: white;
- border-color: #cccccc;
- border-width: 1px;
- border-style: solid;
padding: 2px;
- margin: 1px 0 0 0;
text-align:left;
}
-
+
div.wicket-aa ul {
list-style:none;
padding: 2px;
Modified: wicket/branches/wicket-1.4.x/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.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js?rev=886735&r1=886734&r2=886735&view=diff
==============================================================================
--- wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js (original)
+++ wicket/branches/wicket-1.4.x/wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/autocomplete/wicket-autocomplete.js Thu Dec 3 10:50:01 2009
@@ -52,10 +52,24 @@
var objonkeypress;
var objonchange;
var objonchangeoriginal;
- var objonfocus;
- var ignoreOneFocusGain = false; // on FF, clicking an option in the popup would make field loose focus; focus() call only has effect in FF after popup is hidden, so the re-focusing must not show popup again in this case
+ // holds the eventual margins, padding, etc. of the menu container.
+ // it is computed when the menu is first rendered, and then reused.
+ var initialDelta = -1;
+ // remember popup container border size so we can use style.width/height = ... correctly; array [horizontal, vertical]
+ var usefulDimensionsInitialized = false;
+ var containerBorderWidths = [0, 0];
+ var scrollbarSize = 0;
+ var selChSinceLastRender = false;
+
+ // this are the last visible and non-temporary bounds of the pop-up; the may change position and size several times when showing/updating choices and so on
+ // before it reaches the bounds that will be visible by the user (this is because of height/width settings limits or because it tries to compute preferred size);
+ // used for IE fix with hideShowCovered() - to be able to call it when bounds change while popup is visible.
+ var lastStablePopupBounds = [0, 0, 0, 0];
+
+ var ignoreOneFocusGain = false; // on FF, clicking an option in the pop-up would make field loose focus; focus() call only has effect in FF after popup is hidden, so the re-focusing must not show popup again in this case
+
// holds a throttler, for not sending many requests if the user types
// too quickly.
var localThrottler = new Wicket.Throttler(true);
@@ -89,7 +103,7 @@
obj.onblur=function(event){
if(mouseactive==1){
- ignoreOneFocusGain = true;
+ ignoreOneFocusGain = true;
Wicket.$(elementId).focus();
return killEvent(event);
}
@@ -98,8 +112,11 @@
}
obj.onfocus=function(event){
- if (mouseactive==1) return killEvent(event);
- if (!ignoreOneFocusGain && cfg.showListOnFocusGain) {
+ if (mouseactive==1) {
+ ignoreOneFocusGain = false;
+ return killEvent(event);
+ }
+ if (!ignoreOneFocusGain && cfg.showListOnFocusGain && visible==0) {
if (cfg.showCompleteListOnFocusGain) {
updateChoices(true);
} else {
@@ -113,20 +130,22 @@
obj.onkeydown=function(event){
switch(wicketKeyCode(Wicket.fixEvent(event))){
case KEY_UP:
- if(selected>-1)selected--;
+ if(selected>-1) setSelected(selected-1);
if(selected==-1){
hideAutoComplete();
} else {
- render();
+ render(true);
}
if(Wicket.Browser.isSafari())return killEvent(event);
break;
case KEY_DOWN:
- if(selected<elementCount-1) selected++;
+ if(selected<elementCount-1){
+ setSelected(selected+1);
+ }
if(visible==0){
updateChoices();
} else {
- render();
+ render(true);
showAutoComplete();
}
if(Wicket.Browser.isSafari())return killEvent(event);
@@ -139,23 +158,20 @@
case KEY_ENTER:
if(selected > -1) {
var value = getSelectedValue();
- if(value = handleSelection(value)) {
+ value = handleSelection(value);
+ hideAutoComplete();
+ hidingAutocomplete = 1;
+ if(value) {
obj.value = value;
if(typeof objonchange=="function") objonchange.apply(this,[event]);
}
- hideAutoComplete();
- hidingAutocomplete = 1;
} else if (Wicket.AutoCompleteSettings.enterHidesWithNoSelection) {
hideAutoComplete();
hidingAutocomplete = 1;
}
- mouseactive=0;
- if(typeof objonkeydown=="function")objonkeydown.apply(this,[event]);
-
- if(selected>-1){
- //return killEvent(event);
- }
- return true;
+ mouseactive = 0;
+ if (typeof objonkeydown=="function") objonkeydown.apply(this,[event]);
+ return true;
break;
default:
}
@@ -163,7 +179,7 @@
obj.onkeyup=function(event){
switch(wicketKeyCode(Wicket.fixEvent(event))){
- case KEY_TAB:
+ case KEY_TAB:
case KEY_ENTER:
return killEvent(event);
case KEY_UP:
@@ -179,7 +195,6 @@
updateChoices();
}
if(typeof objonkeyup=="function")objonkeyup.apply(this,[event]);
- return null;
}
obj.onkeypress=function(event){
@@ -192,13 +207,20 @@
if(typeof objonkeypress=="function")objonkeypress.apply(this,[event]);
}
}
+
+ function setSelected(newSelected) {
+ if (newSelected != selected) {
+ selected = newSelected;
+ selChSinceLastRender = true;
+ }
+ }
function handleSelection(input) {
- var menu = getAutocompleteMenu();
- var attr = menu.firstChild.childNodes[selected].attributes['onselect'];
- return attr ? eval(attr.value) : input;
+ var menu = getAutocompleteMenu();
+ var attr = menu.firstChild.childNodes[selected].attributes['onselect'];
+ return attr ? eval(attr.value) : input;
}
-
+
function getMenuId() {
return elementId+"-autocomplete";
}
@@ -213,7 +235,9 @@
document.body.appendChild(container);
container.style.display="none";
container.style.overflow="auto";
- container.style.position="absolute";
+ container.style.position="absolute";
+ container.style.margin="0px"; // this needs to be 0 or size/location calculations would not be exact
+ container.style.padding="0px"; // this needs to be 0 or size/location calculations would not be exact
container.id=getMenuId()+"-container";
container.show = function() { wicketShow(this.id) };
@@ -259,7 +283,7 @@
}
function updateChoices(showAll){
- selected=-1;
+ setSelected(-1);
if (showAll) {
localThrottler.throttle(getMenuId(), throttleDelay, actualUpdateChoicesShowAll);
} else {
@@ -278,7 +302,8 @@
{
showIndicator();
var value = wicketGet(elementId).value;
- var request = new Wicket.Ajax.Request(callbackUrl+(callbackUrl.indexOf("?")>-1 ? "&" : "?") + "q="+processValue(value), doUpdateChoices, false, true, false, "wicket-autocomplete|d"); request.get();
+ var request = new Wicket.Ajax.Request(callbackUrl+(callbackUrl.indexOf("?")>-1 ? "&" : "?") + "q="+processValue(value), doUpdateChoices, false, true, false, "wicket-autocomplete|d");
+ request.get();
}
function showIndicator() {
@@ -298,33 +323,191 @@
}
function showAutoComplete(){
- var position=getPosition(wicketGet(elementId));
+ var input = wicketGet(elementId);
var container = getAutocompleteContainer();
- var input=wicketGet(elementId);
var index=getOffsetParentZIndex(elementId);
container.show();
if (!isNaN(new Number(index))) {
container.style.zIndex=(new Number(index)+1);
- }
- container.style.left=position[0]+'px';
- container.style.top=(input.offsetHeight+position[1])+'px';
- if(cfg.adjustInputWidth)
- container.style.width=input.offsetWidth+'px';
- visible=1;
- hideShowCovered();
+ }
+ if (!usefulDimensionsInitialized)
+ {
+ initializeUsefulDimensions(input, container);
+ }
+ if (cfg.adjustInputWidth) {
+ var newW = input.offsetWidth-containerBorderWidths[0];
+ container.style.width = (newW >= 0 ? newW : input.offsetWidth)+'px';
+ }
+
+ calculateAndSetPopupBounds(input, container);
+
+ if (visible == 0) {
+ visible = 1;
+ hideShowCovered(true, lastStablePopupBounds[0], lastStablePopupBounds[1], lastStablePopupBounds[2], lastStablePopupBounds[3]);
+ }
+ }
+
+ function initializeUsefulDimensions(input, container) {
+ usefulDimensionsInitialized = true;
+ // a few checks to increase the odds that we can count on clientWidth/Height
+ if (typeof (container.clientWidth) != "undefined" && typeof (container.clientHeight) != "undefined" && container.clientWidth > 0 && container.clientHeight > 0) {
+ var tmp = container.style.overflow; // clientWidth & clientHeight exclude border and scollbars
+ container.style.overflow = "visible";
+ containerBorderWidths[0] = container.offsetWidth - container.clientWidth;
+ containerBorderWidths[1] = container.offsetHeight - container.clientHeight;
+
+ if (cfg.useSmartPositioning) {
+ container.style.overflow = "scroll";
+ scrollbarSize = container.offsetWidth - container.clientWidth - containerBorderWidths[0];
+ }
+ // although border is computed correctly, resizing needs 1 less px on FF for some dubious reason...
+ if (Wicket.Browser.isGecko() && containerBorderWidths[0] > 0 && containerBorderWidths[1] > 0) {
+ containerBorderWidths[0]--;
+ containerBorderWidths[1]--;
+ }
+
+ container.style.overflow = tmp;
+ }
}
function hideAutoComplete(){
visible=0;
- selected=-1;
- if ( getAutocompleteContainer() )
+ setSelected(-1);
+ mouseactive=0;
+ var container = getAutocompleteContainer();
+ if (container)
{
- getAutocompleteContainer().hide();
- hideShowCovered();
+ hideShowCovered(false, lastStablePopupBounds[0], lastStablePopupBounds[1], lastStablePopupBounds[2], lastStablePopupBounds[3]);
+ container.hide();
+ if (!cfg.adjustInputWidth && container.style.width != "auto") {
+ container.style.width = "auto"; // let browser auto-set width again next time it is shown
+ }
+ }
+ }
+
+ function getWindowWidthAndHeigth() {
+ var myWidth = 0, myHeight = 0;
+ if( typeof( window.innerWidth ) == 'number' ) {
+ //Non-IE
+ myWidth = window.innerWidth;
+ myHeight = window.innerHeight;
+ } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
+ //IE 6+ in 'standards compliant mode'
+ myWidth = document.documentElement.clientWidth;
+ myHeight = document.documentElement.clientHeight;
+ } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
+ //IE 4 compatible
+ myWidth = document.body.clientWidth;
+ myHeight = document.body.clientHeight;
+ }
+ return [ myWidth, myHeight ];
+ }
+
+ function getWindowScrollXY() {
+ var scrOfX = 0, scrOfY = 0;
+ if( typeof( window.pageYOffset ) == 'number' ) {
+ //Netscape compliant
+ scrOfY = window.pageYOffset;
+ scrOfX = window.pageXOffset;
+ } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
+ //DOM compliant
+ scrOfY = document.body.scrollTop;
+ scrOfX = document.body.scrollLeft;
+ } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
+ //IE6 standards compliant mode
+ scrOfY = document.documentElement.scrollTop;
+ scrOfX = document.documentElement.scrollLeft;
+ }
+ return [ scrOfX, scrOfY ];
+ }
+
+ function calculateAndSetPopupBounds(input, popup)
+ {
+ var leftPosition=0;
+ var topPosition=0;
+ var inputPosition=getPosition(input);
+ if (cfg.useSmartPositioning) {
+ // there are 4 possible positions for the popup: top-left, top-right, buttom-left, bottom-right
+ // relative to the field; we will try to use the position that does not get out of the visible page
+ if (popup.style.width == "auto") {
+ popup.style.left = "0px"; // allow browser to stretch div as much as needed to see where the popup should be put
+ popup.style.top = "0px";
+ }
+ var windowScrollXY = getWindowScrollXY();
+ var windowWH = getWindowWidthAndHeigth();
+ var windowScrollX = windowScrollXY[0];
+ var windowScrollY = windowScrollXY[1];
+ var windowWidth = windowWH[0];
+ var windowHeight = windowWH[1];
+
+ var dx1 = windowScrollX + windowWidth - inputPosition[0] - popup.offsetWidth;
+ var dx2 = inputPosition[0] + input.offsetWidth - popup.offsetWidth - windowScrollX;
+ if (popup.style.width == "auto" && dx1 < 0 && dx2 < 0) {
+ // browser determined popup width; if it does not fit either right or left aligned with the input, calculate and set fixed width
+ // so that after initial position calculation after popup opens, bounds do not change every time a mouse over or other event happens.
+ // The browser can change the width/height when div if repositioned - if they were not already restricted because of maxHeight and field width (and that can result in a relocation of the div and so on).
+ var newW = popup.offsetWidth + Math.max(dx1, dx2) - containerBorderWidths[0];
+ popup.style.width = (newW >= 0 ? newW : popup.offsetWidth + Math.max(dx1, dx2))+'px';
+ dx1 = windowScrollX + windowWidth - inputPosition[0] - popup.offsetWidth;
+ dx2 = inputPosition[0] + input.offsetWidth - popup.offsetWidth - windowScrollX;
+ }
+
+ var dy1 = windowScrollY + windowHeight - inputPosition[1] - input.offsetHeight - popup.offsetHeight;
+ var dy2 = inputPosition[1] - popup.offsetHeight - windowScrollY;
+ if (dy1 < 0 && dy2 < 0) {
+ // limit height if it gets outside the screen
+ var newH = popup.offsetHeight + Math.max(dy1, dy2) - containerBorderWidths[1];
+ popup.style.height = (newH >= 0 ? newH : popup.offsetHeight + Math.max(dy1, dy2))+'px';
+ var dy1 = windowScrollY + windowHeight - inputPosition[1] - input.offsetHeight - popup.offsetHeight;
+ var dy2 = inputPosition[1] - popup.offsetHeight - windowScrollY;
+ }
+
+ // choose the location that shows the most surface of the popup, with preference for bottom right
+ if (dx1 < 0 && dx1 < dx2) {
+ if (dy1 < 0 && dy1 < dy2) {
+ // choice 4 : top left
+ leftPosition = inputPosition[0] + input.offsetWidth - popup.offsetWidth;
+ topPosition = inputPosition[1] - popup.offsetHeight;
+ } else {
+ // choice 3 : bottom left
+ leftPosition = inputPosition[0] + input.offsetWidth - popup.offsetWidth;
+ topPosition = inputPosition[1] + input.offsetHeight;
+ }
+ } else {
+ if (dy1 < 0 && dy1 < dy2) {
+ // choice 2 : top right
+ leftPosition = inputPosition[0];
+ topPosition = inputPosition[1] - popup.offsetHeight;
+ } else {
+ // choice 1 : bottom right
+ leftPosition = inputPosition[0];
+ topPosition = inputPosition[1] + input.offsetHeight;
+ }
+ }
+ if (popup.style.width == "auto") {
+ var newW = popup.offsetWidth - containerBorderWidths[0];
+ popup.style.width = (newW >= 0 ? newW : popup.offsetWidth)+'px';
+ }
+ } else {
+ leftPosition = inputPosition[0];
+ topPosition = inputPosition[1] + input.offsetHeight;
+ }
+ popup.style.left=leftPosition+'px';
+ popup.style.top=topPosition+'px';
+
+ if (visible == 1 &&
+ (lastStablePopupBounds[0] != popup.offsetLeft ||
+ lastStablePopupBounds[1] != popup.offsetTop ||
+ lastStablePopupBounds[2] != popup.offsetWidth ||
+ lastStablePopupBounds[3] != popup.offsetHeight)) {
+ hideShowCovered(false, lastStablePopupBounds[0], lastStablePopupBounds[1], lastStablePopupBounds[2], lastStablePopupBounds[3]); // show previously hidden
+ hideShowCovered(true, popup.offsetLeft, popup.offsetTop, popup.offsetWidth, popup.offsetHeight); // hide ones under new bounds
}
+
+ lastStablePopupBounds = [popup.offsetLeft, popup.offsetTop, popup.offsetWidth, popup.offsetHeight];
}
- function getPosition(obj) {
+ function getPosition(obj) {
var leftPosition = obj.offsetLeft || 0;
var topPosition = obj.offsetTop || 0;
obj = obj.offsetParent;
@@ -335,7 +518,7 @@
leftPosition -= obj.scrollLeft || 0;
obj = obj.offsetParent;
}
-
+
return [leftPosition,topPosition];
}
@@ -349,53 +532,55 @@
hideIndicator();
return;
}
-
+
var element = getAutocompleteMenu();
+ if (!cfg.adjustInputWidth && element.parentNode && element.parentNode.style.width != "auto") {
+ element.parentNode.style.width = "auto"; // let browser auto-set width again as displayed elements may change
+ selChSinceLastRender = true; // selected item will not have selected style until rendrered
+ }
element.innerHTML=resp;
if(element.firstChild && element.firstChild.childNodes) {
- elementCount=element.firstChild.childNodes.length;
+ elementCount=element.firstChild.childNodes.length;
- var clickFunc = function(event){
- mouseactive=0;
- var value = getSelectedValue();
- if(value = handleSelection(value)) {
- wicketGet(elementId).value=value;
- if (typeof objonchange=="function") {objonchange.apply(wicketGet(elementId), [event]);}
- }
- hideAutoComplete();
- if (Wicket.Focus.getFocusedElement() != input)
- {
+ var clickFunc = function(event) {
+ mouseactive = 0;
+ var value = getSelectedValue();
+ var input = wicketGet(elementId);
+ if(value = handleSelection(value)) {
+ input.value = value;
+ if(typeof objonchange=="function") objonchange.apply(input,[event]);
+ }
+ hideAutoComplete();
+ if (Wicket.Focus.getFocusedElement() != input) {
ignoreOneFocusGain = true;
input.focus();
}
- };
+ };
- var mouseOverFunc = function(event){
- mouseactive=1; // Opera needs this as mousemove for menu is not always triggered
- selected = getElementIndex(this);
- render();
- showAutoComplete();
- };
-
- var parentNode = element.firstChild;
- for(var i = 0;i < elementCount; i++) {
- var node = parentNode.childNodes[i];
- node.onclick = clickFunc;
- node.onmouseover = mouseOverFunc;
- }
+ var mouseOverFunc = function(event) {
+ setSelected(getElementIndex(this));
+ render(false); // don't scroll - breaks mouse weel scrolling
+ showAutoComplete();
+ };
+ var parentNode = element.firstChild;
+ for(var i = 0;i < elementCount; i++) {
+ var node = parentNode.childNodes[i];
+ node.onclick = clickFunc;
+ node.onmouseover = mouseOverFunc;
+ }
} else {
elementCount=0;
}
if(elementCount>0){
- if(cfg.preselect==true){
- selected = 0;
- }
- showAutoComplete();
+ if(cfg.preselect==true){
+ setSelected(0);
+ }
+ showAutoComplete();
} else {
hideAutoComplete();
}
- render();
+ render(false);
scheduleEmptyCheck();
@@ -437,31 +622,33 @@
return str.replace(/<[^>]+>/g,"");
}
- function adjustScrollOffset(menu, item) {
+ function adjustScrollOffset(menu, item) { // this should consider margins/paddings; now it is not exact
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(){
+ function render(adjustScroll){
var menu=getAutocompleteMenu();
var height=0;
var node=menu.firstChild.childNodes[0];
var re = /\bselected\b/gi;
+ var sizeAffected = false;
for(var i=0;i<elementCount;i++)
{
var origClassNames = node.className;
var classNames = origClassNames.replace(re, "");
if(selected==i){
classNames += " selected";
- adjustScrollOffset(menu.parentNode, node);
+ if (adjustScroll) adjustScrollOffset(menu.parentNode, node);
}
- if (classNames != origClassNames)
+ if (classNames != origClassNames) {
node.className = classNames;
+ }
if (cfg.maxHeight > -1)
height+=node.offsetHeight;
@@ -469,12 +656,27 @@
node = node.nextSibling;
}
if (cfg.maxHeight > -1) {
- if (height<cfg.maxHeight) {
- menu.parentNode.style.height="auto";
- } else {
- menu.parentNode.style.height=cfg.maxHeight+"px";
+ if (initialDelta == -1)
+ {
+ // remember size occupied by parent border, padding, and menu+ul margins, border, padding
+ initialDelta = menu.parentNode.offsetHeight - height;
+ }
+ if (height + initialDelta > cfg.maxHeight) {
+ var newH = cfg.maxHeight - containerBorderWidths[1];
+ menu.parentNode.style.height = (newH >= 0 ? newH : cfg.maxHeight) + "px";
+ sizeAffected = true;
+ } else if (menu.parentNode.style.height != "auto") { // if height is limited
+ menu.parentNode.style.height = "auto"; // no limiting, let popup determine it's own height
+ sizeAffected = true;
}
}
+ if (cfg.useSmartPositioning && !cfg.adjustInputWidth && menu.parentNode.style.width != "auto" && selChSinceLastRender) {
+ // selected item has different padding - so the preferred width of the popup might want to change so as not to wrap it
+ selChSinceLastRender = false;
+ menu.parentNode.style.width = "auto";
+ sizeAffected = true;
+ }
+ if (sizeAffected) calculateAndSetPopupBounds(wicketGet(elementId), menu.parentNode); // update stuff related to bounds if needed
}
// From http://www.robertnyman.com/2006/04/24/get-the-rendered-style-of-an-element/
@@ -511,49 +713,35 @@
return index;
}
- function hideShowCovered(){
- if (!/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
+ function hideShowCovered(popupVisible, acLeftX, acTopY, acWidth, acHeight) {
+ if (!cfg.useHideShowCoveredIEFix || (!/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent))) {
return;
}
- // IE7 fix, if this doesn't go in a timeout then the complete page could become invisible.
- // when closing the popup.
- setTimeout(hideShowCoveredTimeout,1);
- }
-
- function hideShowCoveredTimeout(){
- var el=getAutocompleteContainer();
- var p=getPosition(el);
-
- var acLeftX=p[0];
- var acRightX=el.offsetWidth+acLeftX;
- var acTopY=p[1];
- var acBottomY=el.offsetHeight+acTopY;
var hideTags=new Array("select","iframe","applet");
+ var acRightX = acLeftX + acWidth;
+ var acBottomY = acTopY + acHeight;
for (var j=0;j<hideTags.length;j++) {
var tagsFound=document.getElementsByTagName(hideTags[j]);
for (var i=0; i<tagsFound.length; i++){
var tag=tagsFound[i];
- p=getPosition(tag);
+ var p=getPosition(tag); // maybe determine only visible area of tag somehow? as it could be in a small scrolled container...
var leftX=p[0];
var rightX=leftX+tag.offsetWidth;
var topY=p[1];
var bottomY=topY+tag.offsetHeight;
- if (this.hidden || (leftX>acRightX) || (rightX<acLeftX) || (topY>acBottomY) || (bottomY<acTopY)) {
- if(!tag.wicket_element_visibility) {
- tag.wicket_element_visibility=isVisible(tag);
- }
- tag.style.visibility=tag.wicket_element_visibility;
+ if (!tag.wicket_element_visibility) {
+ tag.wicket_element_visibility=isVisible(tag);
+ }
+ if (popupVisible==0 || (leftX>acRightX) || (rightX<acLeftX) || (topY>acBottomY) || (bottomY<acTopY)) {
+ tag.style.visibility = tag.wicket_element_visibility;
} else {
- if (!tag.wicket_element_visibility) {
- tag.wicket_element_visibility=isVisible(tag);
- }
tag.style.visibility = "hidden";
}
}
- }
+ }
}
initialize();