AutoCompleteTag.java

package sk.iway.iwcm.tags;

import static sk.iway.iwcm.Tools.isInteger;
import static sk.iway.iwcm.Tools.isNotEmpty;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTag;

import org.json.JSONArray;

import net.sourceforge.stripes.exception.StripesJspException;
import net.sourceforge.stripes.tag.FormTag;
import net.sourceforge.stripes.tag.InputTagSupport;
import net.sourceforge.stripes.validation.ValidationMetadata;


/**
 *  AutoCompleteTag.java
 *
 *		Tag designed to ease creation of tags with auto completing capabilities.
 *		The generated tag acts as a common input type="text" tag, it shares all the common
 *		HTML options. Any HTML attribute is passed as-is to the output.
 *		Attributes affecting auto completing capabilities:
 *<p>
 *		id: DOM id. Passed to jQuery selector to identify the object
 *</p><p>
 *		name: defines parameter name that the receiving script is about to access the search term by means of request.getParameter()
 *</p><p>
 *		url: URL of the receiving script. This URL MAY contain a query string
 *</p><p>
 *		urlParams: used in case we want to alter autocomplete list dynamically based on other values that merely the search term itself.
 *			Pass them in form of a jQuery selectors separated by comma ','. Do NOT include apostrophes
 *</p><p>
 *		onOptionSelect: a function to post-process a value once it is chosen from the autocomplete list. Two arguments are passed to the function - event and ui.
 *				ui is an object encapsulating the chosen value. Search term can be accessed by calling 'ui.item.value'. Pass only the function's name, WITHOUT BRACES!
 *</p>
 *
 *	The receiving script is expected to return a javascript-compliant array, one that can be easily generated by {@link JSONArray}.toString(). This array is
 *	passed into javascript's eval() function and is expected to form an array after calling eval().
 *
 *		Referential use: conf_editor_popup.jsp + _conf_autocomplete.jsp
 *
 *	Dependencies:
 *		jquery.js
 *		jquery-ui-core.js
 *		jquery-ui-autocomplete.js
 *		ajax_support.js
 *
 *
 *@Title        webjet7
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2010
 *@author       $Author: marosurbanec $
 *@version      $Revision: 1.3 $
 *@created      Date: 14.07.2010 16:19:42
 *@modified     $Date: 2004/08/16 06:26:11 $
 */
public class AutoCompleteTag extends InputTagSupport implements BodyTag
{

	public AutoCompleteTag()
	{
		getAttributes().put("type", "text");
	}

	String onOptionSelect;
	String urlParams;
	String placeholder;
	String url;
	String value;
	String maxRows;
	String maxLength;
	String minLength;

	public String getMinLength()
	{
		return minLength;
	}

	public void setMinLength(String minLength)
	{
		this.minLength = minLength;
	}

	public String getMaxRows()
	{
		return maxRows;
	}

	public void setMaxRows(String maxRows)
	{
		this.maxRows = maxRows;
	}

	public String getValue()
	{
		if (value == null) return "";
		return value;
	}

	public void setValue(String value)
	{
		this.value = value;
	}

	public String getUrl()
	{
		return url;
	}

	public void setUrl(String url)
	{
		this.url = url;
	}

	public String getOnOptionSelect()
	{
		if (onOptionSelect == null) return "";
		return onOptionSelect;
	}

	public void setOnOptionSelect(String onOptionSelect)
	{
		this.onOptionSelect = onOptionSelect;
	}

	public String getUrlParams()
	{
		if (urlParams == null) return "";
		return urlParams;
	}

	public void setUrlParams(String urlParams)
	{
		this.urlParams = urlParams;
	}

	/**
	 * Inserts an input-text tag with necessary javascript libraries
	 */
	@Override
	public int doEndInputTag() throws JspException
	{
		getAttributes().put("value", getValue());
		getAttributes().put("maxLength", maxLength);
		getAttributes().put("placeholder", getPlaceholder());
		try
		{
			JspWriter out = pageContext.getOut();
			HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
			set("autocomplete", "off");
			//najskor sa muzi sapisat input tag, az potom ostatne JS (aby existoval ked ma)
			writeSingletonTag(out, "input");

			insertAjaxSupport(out, request);
			//unpacked autocomplete is bugged, doesn't work properly (fires off JS error), insert packed version
			insertUIcore(out,request);
			insertAutoComplete(out, request);
			//out.println(jsText());
			ReadWriteScriptBean bean = new ReadWriteScriptBean("text/javascript", null, null, null, null, jsBody(), null, null, "AutoCompleteTag.java");
			//out.println("<script type='text/javascript' src='/components/_common/javascript/ajax_support.js'></script>");
			ReadWriteScriptTag.addScriptFromClass( bean, request, out);
		}
		catch (IOException e)
		{
			sk.iway.iwcm.Logger.error(e);
		}

		return EVAL_PAGE;
	}

	/**
	 *
	 */
	private void insertUIcore(JspWriter out, HttpServletRequest request) throws IOException
	{
		boolean coreInserted = isNotEmpty( (String)(request.getAttribute("jQueryUI-core-Inserted")) );
		request.setAttribute("jQueryUI-core-Inserted", "true");
		if(!coreInserted)
		{
			ReadWriteScriptBean bean = null;
			if ("iwcm.interway.sk".equals(request.getServerName()))
			{
				bean = new ReadWriteScriptBean("text/javascript", "/components/_common/javascript/jqui/jquery-ui-core.js", null, null, null, null, null, null, "AutoCompleteTag.java", null, null);
				//out.println("<script type=\"text/javascript\" src=\"/components/_common/javascript/jqui/jquery-ui-core.js\" ></script>");

			}
			else
			{
				bean = new ReadWriteScriptBean("text/javascript", "/components/_common/javascript/jqui/jquery-ui-core.min.js", null, null, null, null, null, null, "AutoCompleteTag.java", null, null);
				//out.println("<script type=\"text/javascript\" src=\"/components/_common/javascript/jqui/jquery-ui-core.min.js\" ></script>");
			}

			ReadWriteScriptTag.addScriptFromClass( bean, request, out);
		}
	}

	/**
	 * Make sure those scripts are present once and only once on a page.
	 */
	private void insertAutoComplete(JspWriter out, HttpServletRequest request) throws IOException
	{
		if (request.getAttribute("autocompleteInserted") == null && request.getAttribute("jQueryUI-autocomplete-Inserted") == null && request.getAttribute("jQueryUI-all-Inserted") == null)
		{
			ReadWriteScriptBean bean = new ReadWriteScriptBean("text/javascript", "/components/_common/javascript/jqui/jquery-ui-autocomplete.min.js", null, null, null, null, null, null, "AutoCompleteTag.java", null, null);
			ReadWriteScriptTag.addScriptFromClass( bean, request, out);
			//out.println("<script type='text/javascript' src='/components/_common/javascript/jqui/jquery-ui-autocomplete.min.js' ></script>");
		}
		request.setAttribute("autocompleteInserted", true);
	}

	private void insertAjaxSupport(JspWriter out, HttpServletRequest request) throws IOException
	{
		if (request.getAttribute("ajaxSupportInserted") == null)
		{
			ReadWriteScriptBean bean = new ReadWriteScriptBean("text/javascript", "/components/_common/javascript/ajax_support.js", null, null, null, null, null, null, "AutoCompleteTag.java", null, null);
			//out.println("<script type='text/javascript' src='/components/_common/javascript/ajax_support.js'></script>");
			ReadWriteScriptTag.addScriptFromClass( bean, request, out);
		}
		request.setAttribute("ajaxSupportInserted", true);
	}

	/**
	 * Utilitises ajax_support.js to create a chained call leading in subscribing a
	 * function hook to document onReady event.
	 */
	/*private String jsText()
	{
		StringBuilder code = new StringBuilder().
		append("<script type='text/javascript'>\n").
		append("new AutoCompleter('#").
		append(getId()).
		append("').setUrl('").
		append(getUrl()).
		append("')");
		if (isNotEmpty(urlParams))
			code.append(".setParams('").append(urlParams).append("')");
		if (isNotEmpty(onOptionSelect))
			code.append(".setOnOptionSelect('").append(onOptionSelect).append("')");
		if (isInteger(maxRows))
			code.append(".setMaxRows(").append(maxRows).append(')');
		if (isInteger(minLength))
			code.append(".setMinLength(").append(minLength).append(')');

		code.append(".transform()\n").append("</script>");

		return code.toString();
	}*/

	private String jsBody()
	{
		StringBuilder code = new StringBuilder().
		//append("<script type='text/javascript'>\n").
		append("new AutoCompleter('#").
		append(getId()).
		append("').setUrl('").
		append(getUrl()).
		append("')");
		if (isNotEmpty(urlParams))
			code.append(".setParams('").append(urlParams).append("')");
		if (isNotEmpty(onOptionSelect))
			code.append(".setOnOptionSelect('").append(onOptionSelect).append("')");
		if (isInteger(maxRows))
			code.append(".setMaxRows(").append(maxRows).append(')');
		if (isInteger(minLength))
			code.append(".setMinLength(").append(minLength).append(')');

		code.append(".transform()\n");//.append("</script>");

		return code.toString();
	}

	//----------------------TO SHUT UP STRIPES COMPLAINTS--------------------
	@Override
	public FormTag getParentFormTag() throws StripesJspException{return null;}
	@Override
	protected ValidationMetadata getValidationMetadata() throws StripesJspException {return new ValidationMetadata("");}
	@Override
	protected String getLocalizedFieldName(String arg0) throws StripesJspException {return "";}
	@Override
	protected void registerWithParentForm() throws StripesJspException {
		//
	}
	@Override
	protected void loadErrors() throws StripesJspException {
		//
	}
	@Override
	public int doStartInputTag() throws JspException
	{
		return EVAL_BODY_BUFFERED;
	}
	@Override
	public void doInitBody() throws JspException{/*nothing*/}
	@Override
	public int doAfterBody() throws JspException{return SKIP_BODY;}

	public String getPlaceholder()
	{
		return placeholder;
	}

	public void setPlaceholder(String placeholder)
	{
		this.placeholder = placeholder;
	}

	public String getMaxLength()
	{
		return maxLength;
	}

	public void setMaxLength(String maxLength)
	{
		this.maxLength = maxLength;
	}


}