All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wicketstuff.objectautocomplete.ObjectAutoCompleteBehavior Maven / Gradle / Ivy

There is a newer version: 10.3.0
Show newest version
/*
 * 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.wicketstuff.objectautocomplete;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AbstractAutoCompleteBehavior;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.request.IRequestCycle;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.resource.CoreLibrariesContributor;

import java.io.Serializable;
import java.util.Iterator;

/**
 * Behaviour for object auto completion using a slightly modified variant of {@see
 * org.apache.wicket.extensions.ajax.markup.html.autocomplete.AbstractAutoCompleteBehavior}
 *
 * An (hidden) element is required to store the object id which has been selected.
 *
 * The type parameter is the type of the object to be rendered (not it's id)
 *
 * @author roland
 * @since May 18, 2008
 */
public class ObjectAutoCompleteBehavior extends AbstractAutoCompleteBehavior
{
	private static final long serialVersionUID = 1L;
	private static final ResourceReference OBJECTAUTOCOMPLETE_JS = new JavaScriptResourceReference(
		ObjectAutoCompleteBehavior.class, "wicketstuff-objectautocomplete.js");
	// Our version of 'wicket-autocomplete.js', with the patch from WICKET-1651
	private static final ResourceReference AUTOCOMPLETE_OBJECTIFIED_JS = new JavaScriptResourceReference(
		ObjectAutoCompleteBehavior.class, "wicketstuff-dropdown-list.js");

	// Element holding the object id as value
	private final Component objectElement;

	private final ObjectAutoCompleteCancelListener cancelListener;
	private final AutoCompletionChoicesProvider choicesProvider;

	// one of this renderer must be set with the response renderer taking precedence
	private final IAutoCompleteRenderer renderer;
	private final ObjectAutoCompleteResponseRenderer responseRenderer;

	// =====================================================================================================
	// Specific configuration options:

	// tag name which indicates the possible choices (typically this is a "li")
	private final String choiceTagName;

	// alignment of menu
	private final ObjectAutoCompleteBuilder.Alignment alignment;

	// width of drop down
	private long width = 0;

	// delay for how long to wait for the update
	private final long delay;

	// whether search should be triggered on paste event
	private final boolean searchOnPaste;

	 ObjectAutoCompleteBehavior(Component pObjectElement,
		ObjectAutoCompleteBuilder pBuilder)
	{
		renderer = pBuilder.autoCompleteRenderer;
		settings = new AutoCompleteSettings().setMaxHeightInPx(pBuilder.maxHeightInPx)
			.setPreselect(pBuilder.preselect)
			.setShowListOnEmptyInput(pBuilder.showListOnEmptyInput);
		choiceTagName = pBuilder.choiceTagName;
		width = pBuilder.width;
		alignment = pBuilder.alignement;

		objectElement = pObjectElement;
		responseRenderer = pBuilder.autoCompleteResponseRenderer;
		cancelListener = pBuilder.cancelListener;
		choicesProvider = pBuilder.choicesProvider;
		searchOnPaste = pBuilder.searchOnPaste;
		delay = pBuilder.delay;
	}

	/**
	 * Temporarily solution until patch from WICKET-1651 is applied. Note, that we avoid a call to
	 * super to avoid the initialization in the direct parent class, but we have to copy over all
	 * other code from the parent,
	 *
	 * @param response
	 *            response to write to
	 */
	@Override
	public void renderHead(Component c, IHeaderResponse response)
	{
		abstractDefaultAjaxBehaviour_renderHead(c, response);
		initHead(response);
	}

    @Override
    protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
        super.updateAjaxAttributes(attributes);
        attributes.setWicketAjaxResponse(false);
        attributes.setDataType("text");
    }

    @Override
	protected void onRequest(final String input, RequestCycle requestCycle)
	{
		IRequestHandler target = new IRequestHandler()
		{
			public void respond(IRequestCycle requestCycle)
			{
				WebResponse response = (WebResponse)requestCycle.getResponse();

				// Determine encoding
				final String encoding = Application.get()
					.getRequestCycleSettings()
					.getResponseRequestEncoding();
				response.setContentType("text/xml; charset=" + encoding);

				// Make sure it is not cached by a
				response.disableCaching();

				Iterator comps = getChoices(input);
				if (responseRenderer != null)
				{
					// there is a dedicated renderer configured
					responseRenderer.onRequest(comps, response, input);
				}
				else
				{
					renderer.renderHeader(response);
					int renderedObjects = 0;
					while (comps.hasNext())
					{
						final O comp = comps.next();
						renderer.render(comp, response, input);
						renderedObjects++;
					}
					renderer.renderFooter(response, renderedObjects);
				}
			}

			public void detach(IRequestCycle requestCycle)
			{
			}

		};
		requestCycle.scheduleRequestHandlerAfterCurrent(target);
	}

	// Copied over from AbstractDefaultAjaxBehaviour.renderHead() until patch
	// in WICKET-1651 gets applied
	private void abstractDefaultAjaxBehaviour_renderHead(Component component, IHeaderResponse response)
	{
		CoreLibrariesContributor.contributeAjax(component.getApplication(), response);
	}

	/**
	 * Initialize response with our own java script
	 *
	 * @param response
	 *            response to write to
	 */
	protected void initHead(IHeaderResponse response)
	{
		response.render(JavaScriptHeaderItem.forReference(AUTOCOMPLETE_OBJECTIFIED_JS));
		response.render(JavaScriptHeaderItem.forReference(OBJECTAUTOCOMPLETE_JS));
		final String id = getComponent().getMarkupId();
		String initJS = String.format("new Wicketstuff.ObjectAutoComplete('%s','%s','%s',%s);", id,
			objectElement.getMarkupId(), getCallbackUrl(), getSettings());
		response.render(OnDomReadyHeaderItem.forScript(initJS));
	}

	@Override
	protected void onComponentTag(ComponentTag tag)
	{
		super.onComponentTag(tag);
		if (cancelListener != null)
		{
			final String keypress =
					"if (event) { var kc=Wicket.Event.keyCode(event); if (kc==27) {" +
						"Wicket.Ajax.get({'u': '" + getCallbackUrl() + "&cancel=true&force=true'});" +
						"return false;" +
					"} else if (kc==13) " +
							"return false; " +
					"else return true;}";
			tag.put("onkeypress", keypress);

//			final String onblur = "Wicket.Ajax.get({'u': '" + getCallbackUrl() + "&cancel=true' }); return false;";
//			tag.put("onblur", onblur);
		}
	}

	@Override
	protected void respond(AjaxRequestTarget target)
	{
		RequestCycle requestCycle = RequestCycle.get();
		boolean cancel = requestCycle.getRequest()
			.getRequestParameters()
			.getParameterValue("cancel")
			.toBoolean();
		boolean force = requestCycle.getRequest()
			.getRequestParameters()
			.getParameterValue("force")
			.toBoolean();

		if (cancelListener != null && cancel)
		{
			cancelListener.searchCanceled(target, force);
		}
		else
		{
			super.respond(target);
		}
	}

	protected Iterator getChoices(String input)
	{
		return choicesProvider.getChoices(input);
	}

	// Create settings
	private CharSequence getSettings()
	{
		StringBuilder builder = new StringBuilder(constructSettingsJS());
		if (choiceTagName != null || alignment != null || width != 0)
		{
			// remove trailing "}"
			builder.setLength(builder.length() - 1);
			if (choiceTagName != null)
			{
				builder.append(",choiceTagName: '").append(choiceTagName.toUpperCase()).append("'");
			}
			if (alignment != null)
			{
				builder.append(",align: '")
					.append(
						alignment == ObjectAutoCompleteBuilder.Alignment.LEFT ? "left" : "right")
					.append("'");
			}
			if (width != 0)
			{
				builder.append(",width: ").append(width);
			}
			if (delay != 0)
			{
				builder.append(",delay: ").append(delay);
			}
			if (searchOnPaste)
			{
				builder.append(",searchOnPaste: true");
			}
			builder.append("}");
		}
		return builder;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy