MYFACES-4301 create shortcut for jsf.ajax.request
diff --git a/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js b/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js
index 8eceb1f..efa761e 100644
--- a/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js
+++ b/api/src/main/javascript/META-INF/resources/myfaces/api/jsf.js
@@ -456,3 +456,28 @@
     //return self;
   }
 }
+
+
+(!window.myfaces) ? window.myfaces = {} : null;
+if (!myfaces.ab) {
+    /*
+     * Shortcut of the jsf.ajax.request, to shorten the rendered JS.
+     */
+    myfaces.ab = function(source, event, eventName, execute, render, options) {
+        if (!options) {
+            options = {};
+        }
+
+        if (eventName) {
+            options["javax.faces.behavior.event"] = eventName;
+        }
+        if (execute) {
+            options["execute"] = execute;
+        }
+        if (render) {
+            options["render"] = render;
+        }
+
+        jsf.ajax.request(source, event, options);
+    };
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
index 15610fb..305d937 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
@@ -18,12 +18,6 @@
  */
 package org.apache.myfaces.renderkit.html;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.RandomAccess;
-
 import javax.faces.FacesException;
 import javax.faces.component.ActionSource;
 import javax.faces.component.EditableValueHolder;
@@ -31,14 +25,11 @@
 import javax.faces.component.behavior.AjaxBehavior;
 import javax.faces.component.behavior.ClientBehavior;
 import javax.faces.component.behavior.ClientBehaviorContext;
-import javax.faces.component.search.SearchExpressionContext;
-import javax.faces.component.search.SearchExpressionHandler;
 import javax.faces.context.FacesContext;
 import javax.faces.event.AjaxBehaviorEvent;
 import javax.faces.event.PhaseId;
 import javax.faces.render.ClientBehaviorRenderer;
-import org.apache.myfaces.component.search.MyFacesSearchExpressionHints;
-import org.apache.myfaces.util.lang.StringUtils;
+import org.apache.myfaces.renderkit.html.base.AjaxScriptBuilder;
 import org.apache.myfaces.util.SharedStringBuilder;
 
 /**
@@ -47,22 +38,9 @@
  */
 public class HtmlAjaxBehaviorRenderer extends ClientBehaviorRenderer
 {
-
-    private static final String AJAX_KEY_ONERROR = "onerror";
-    private static final String AJAX_KEY_ONEVENT = "onevent";
-    private static final String AJAX_KEY_EXECUTE = "execute";
-    private static final String AJAX_KEY_RENDER = "render";
-    private static final String AJAX_KEY_DELAY = "delay";
-    private static final String AJAX_KEY_RESETVALUES = "resetValues";
-
-    private static final String AJAX_VAL_THIS = "this";
-    private static final String AJAX_VAL_EVENT = "event";
-    private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
-
     private static final String ERR_NO_AJAX_BEHAVIOR = "The behavior must be an instance of AjaxBehavior";
 
     private static final String AJAX_SB = "oam.renderkit.AJAX_SB";
-    private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";
 
     @Override
     public void decode(FacesContext context, UIComponent component, ClientBehavior behavior)
@@ -88,14 +66,29 @@
             return null;
         }
 
-        return makeAjax(behaviorContext, ajaxBehavior).toString();
+        StringBuilder retVal = SharedStringBuilder.get(behaviorContext.getFacesContext(), AJAX_SB, 60);
+
+        AjaxScriptBuilder.build(behaviorContext.getFacesContext(),
+                retVal,
+                behaviorContext.getComponent(),
+                behaviorContext.getSourceId(),
+                behaviorContext.getEventName(),
+                ajaxBehavior.getExecute(),
+                ajaxBehavior.getRender(),
+                ajaxBehavior.getDelay(),
+                ajaxBehavior.isResetValues(),
+                ajaxBehavior.getOnerror(),
+                ajaxBehavior.getOnevent(),
+                behaviorContext.getParameters());
+  
+        return retVal.toString();
     }
 
     private void dispatchBehaviorEvent(UIComponent component, AjaxBehavior ajaxBehavior)
     {
         AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, ajaxBehavior);
 
-        boolean isImmediate = false;
+        boolean isImmediate;
         if (ajaxBehavior.isImmediateSet())
         {
             isImmediate = ajaxBehavior.isImmediate();
@@ -127,344 +120,6 @@
         return isImmediate;
     }
 
-
-    /**
-     * builds the generic ajax call depending upon
-     * the ajax behavior parameters
-     *
-     * @param context  the Client behavior context
-     * @param behavior the behavior
-     * @return a fully working javascript with calls into jsf.js
-     */
-    private StringBuilder makeAjax(ClientBehaviorContext context, AjaxBehavior behavior)
-    {
-        StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);
-        StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);
-
-        String executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, behavior.getExecute());
-        String render = mapToString(context, paramBuffer, AJAX_KEY_RENDER, behavior.getRender());
-
-        String onError = behavior.getOnerror();
-        if (StringUtils.isNotBlank(onError))
-        {
-            paramBuffer.setLength(0);
-            paramBuffer.append(AJAX_KEY_ONERROR);
-            paramBuffer.append(':');
-            paramBuffer.append(onError);
-            onError = paramBuffer.toString();
-        }
-        else
-        {
-            onError = null;
-        }
-        
-        String onEvent = behavior.getOnevent();
-        if (StringUtils.isNotBlank(onEvent))
-        {
-            paramBuffer.setLength(0);
-            paramBuffer.append(AJAX_KEY_ONEVENT);
-            paramBuffer.append(':');
-            paramBuffer.append(onEvent);
-            onEvent = paramBuffer.toString();
-        }
-        else
-        {
-            onEvent = null;
-        }
-
-        String delay = behavior.getDelay();
-        if (StringUtils.isNotBlank(delay))
-        {
-            paramBuffer.setLength(0);
-            paramBuffer.append(AJAX_KEY_DELAY);
-            paramBuffer.append(':');
-            if ("none".equals(delay))
-            {
-                paramBuffer.append('\'');
-                paramBuffer.append(delay);
-                paramBuffer.append('\'');
-            }
-            else
-            {
-                paramBuffer.append(delay);
-            }
-            delay = paramBuffer.toString();
-        }
-        else
-        {
-            delay = null;
-        }
-
-        String resetValues = Boolean.toString(behavior.isResetValues());
-        if (resetValues.equals("true"))
-        {
-            paramBuffer.setLength(0);
-            paramBuffer.append(AJAX_KEY_RESETVALUES);
-            paramBuffer.append(':');
-            paramBuffer.append(resetValues);
-            resetValues = paramBuffer.toString();
-        }
-        else
-        {
-            resetValues = null;
-        }
-
-        String sourceId;
-        if (context.getSourceId() == null)
-        {
-            sourceId = AJAX_VAL_THIS;
-        }
-        else
-        {
-            paramBuffer.setLength(0);
-            paramBuffer.append('\'');
-            paramBuffer.append(context.getSourceId());
-            paramBuffer.append('\'');
-            sourceId = paramBuffer.toString();
-
-            if (!context.getSourceId().trim().equals(
-                context.getComponent().getClientId(context.getFacesContext())))
-            {
-                // Check if sourceId is not a clientId and there is no execute set
-                UIComponent ref = context.getComponent();
-                ref = (ref.getParent() == null) ? ref : ref.getParent();
-                UIComponent instance = null;
-                try
-                {
-                    instance = ref.findComponent(context.getSourceId());
-                }
-                catch (IllegalArgumentException e)
-                {
-                    // No Op
-                }
-                if (instance == null && executes == null)
-                {
-                    // set the clientId of the component so the behavior can be decoded later,
-                    // otherwise the behavior will fail
-                    List<String> list = new ArrayList<>();
-                    list.add(context.getComponent().getClientId(context.getFacesContext()));
-                    executes = mapToString(context, paramBuffer, AJAX_KEY_EXECUTE, list);
-                }
-            }
-        }
-
-
-        String event = context.getEventName();
-
-        retVal.append(JS_AJAX_REQUEST);
-        retVal.append('(');
-        retVal.append(sourceId);
-        retVal.append(',');
-        retVal.append(AJAX_VAL_EVENT);
-        retVal.append(',');
-
-        Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
-        int paramSize = (params != null) ? params.size() : 0;
-
-        List<String> parameterList = new ArrayList<>(paramSize + 2);
-        if (executes != null)
-        {
-            parameterList.add(executes);
-        }
-        if (render != null)
-        {
-            parameterList.add(render);
-        }
-        if (onError != null)
-        {
-            parameterList.add(onError);
-        }
-        if (onEvent != null)
-        {
-            parameterList.add(onEvent);
-        }
-        /*
-         * since version 2.2
-         */
-        if (delay != null)
-        {
-            parameterList.add(delay);
-        }
-        /*
-         * since version 2.2
-         */
-        if (resetValues != null)
-        {
-            parameterList.add(resetValues);
-        }
-        if (paramSize > 0)
-        {
-            /**
-             * see ClientBehaviorContext.html of the spec
-             * the param list has to be added in the post back
-             */
-            // params are in 99% RamdonAccess instace created in
-            // HtmlRendererUtils.getClientBehaviorContextParameters(Map<String, String>)
-            if (params instanceof RandomAccess)
-            {
-                List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;
-                for (int i = 0, size = list.size(); i < size; i++)
-                {
-                    ClientBehaviorContext.Parameter param = list.get(i);
-                    append(paramBuffer, parameterList, param);
-                }
-            }
-            else
-            {
-                for (ClientBehaviorContext.Parameter param : params)
-                {
-                    append(paramBuffer, parameterList, param);
-                }
-            }
-        }
-
-        paramBuffer.setLength(0);
-        paramBuffer.append('\'');
-        paramBuffer.append(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);
-        paramBuffer.append('\'');
-        paramBuffer.append(':');
-        paramBuffer.append('\'');
-        paramBuffer.append(event);
-        paramBuffer.append('\'');
-        parameterList.add(paramBuffer.toString());
-
-        /**
-         * I assume here for now that the options are the same which also
-         * can be sent via the options attribute to javax.faces.ajax
-         * this still needs further clarifications but I assume so for now
-         */
-        retVal.append(buildOptions(paramBuffer, parameterList));
-
-        retVal.append(')');
-
-        return retVal;
-    }
-
-    private void append(StringBuilder paramBuffer, List<String> parameterList, ClientBehaviorContext.Parameter param)
-    {
-        // Both name and value should be quoted
-        paramBuffer.setLength(0);
-        paramBuffer.append('\'');
-        paramBuffer.append(param.getName());
-        paramBuffer.append('\'');
-        paramBuffer.append(':');
-        paramBuffer.append('\'');
-        paramBuffer.append(param.getValue().toString());
-        paramBuffer.append('\'');
-        parameterList.add(paramBuffer.toString());
-    }
-
-
-    private StringBuilder buildOptions(StringBuilder retVal, List<String> options)
-    {
-        retVal.setLength(0);
-
-        retVal.append('{');
-
-        boolean first = true;
-
-        for (int i = 0, size = options.size(); i < size; i++)
-        {
-            String option = options.get(i);
-            if (StringUtils.isNotBlank(option))
-            {
-                if (!first)
-                {
-                    retVal.append(',');
-                }
-                else
-                {
-                    first = false;
-                }
-                retVal.append(option);
-            }
-        }
-        retVal.append('}');
-        return retVal;
-    }
-
-    private String mapToString(ClientBehaviorContext context, StringBuilder retVal,
-            String target, Collection<String> dataHolder)
-    {
-        //Clear buffer
-        retVal.setLength(0);
-
-        if (dataHolder == null)
-        {
-            dataHolder = Collections.emptyList();
-        }
-
-        int executeSize = dataHolder.size();
-        if (executeSize > 0)
-        {
-            retVal.append(target);
-            retVal.append(':');
-            retVal.append('\'');
-
-            int cnt = 0;
-
-            SearchExpressionContext searchExpressionContext = null;
-            
-            // perf: dataHolder is a Collection : ajaxBehaviour.getExecute()
-            // and ajaxBehaviour.getRender() API
-            // In most cases comes here a ArrayList, because
-            // javax.faces.component.behavior.AjaxBehavior.getCollectionFromSpaceSplitString
-            // creates it.
-            if (dataHolder instanceof RandomAccess)
-            {
-                List<String> list = (List<String>) dataHolder;
-                for (; cnt  < executeSize; cnt++)
-                {
-                    if (searchExpressionContext == null)
-                    {
-                        searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
-                                context.getFacesContext(), context.getComponent(),
-                                MyFacesSearchExpressionHints.SET_RESOLVE_CLIENT_SIDE_RESOLVE_SINGLE_COMPONENT, null);
-                    }
-                    
-                    String strVal = list.get(cnt);
-                    build(context, executeSize, retVal, cnt, strVal, searchExpressionContext);
-                }
-            }
-            else
-            {
-                for (String strVal : dataHolder)
-                {
-                    if (searchExpressionContext == null)
-                    {
-                        searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(
-                                context.getFacesContext(), context.getComponent(),
-                                MyFacesSearchExpressionHints.SET_RESOLVE_CLIENT_SIDE_RESOLVE_SINGLE_COMPONENT, null);
-                    }
-                    
-                    cnt++;
-                    build(context, executeSize, retVal, cnt, strVal, searchExpressionContext);
-                }
-            }
-
-            retVal.append('\'');
-            return retVal.toString();
-        }
-        return null;
-
-    }
-
-    public void build(ClientBehaviorContext context,
-            int size, StringBuilder retVal, int cnt,
-            String strVal, SearchExpressionContext searchExpressionContext)
-    {
-        if (StringUtils.isNotBlank(strVal))
-        {
-            SearchExpressionHandler handler = context.getFacesContext().getApplication().getSearchExpressionHandler();
-            String clientId = handler.resolveClientId(searchExpressionContext, strVal.trim());
-            retVal.append(clientId);
-            if (cnt < size)
-            {
-                retVal.append(' ');
-            }
-        }
-    }
-
     private void assertBehavior(ClientBehavior behavior)
     {
         if (!(behavior instanceof AjaxBehavior))
diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java
index a8afb13..643be6b 100644
--- a/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlCommandScriptRenderer.java
@@ -20,24 +20,19 @@
 package org.apache.myfaces.renderkit.html;

 

 import java.io.IOException;

-import java.util.ArrayList;

-import java.util.Collection;

 import java.util.List;

 import java.util.Map;

-import java.util.RandomAccess;

 import javax.faces.component.UIComponent;

 import javax.faces.component.UIForm;

 import javax.faces.component.UIParameter;

 import javax.faces.component.behavior.ClientBehaviorContext;

 import javax.faces.component.behavior.ClientBehaviorHolder;

 import javax.faces.component.html.HtmlCommandScript;

-import javax.faces.component.search.SearchExpressionContext;

-import javax.faces.component.search.SearchExpressionHandler;

 import javax.faces.context.FacesContext;

 import javax.faces.context.ResponseWriter;

 import javax.faces.event.ActionEvent;

 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFRenderer;

-import org.apache.myfaces.component.search.MyFacesSearchExpressionHints;

+import org.apache.myfaces.renderkit.html.base.AjaxScriptBuilder;

 import org.apache.myfaces.renderkit.html.base.ClientBehaviorRendererUtils;

 import org.apache.myfaces.renderkit.html.util.HTML;

 import org.apache.myfaces.renderkit.html.base.HtmlRenderer;

@@ -52,17 +47,7 @@
 @JSFRenderer(renderKitId = "HTML_BASIC", family = "javax.faces.Command", type = "javax.faces.Script")

 public class HtmlCommandScriptRenderer extends HtmlRenderer

 {

-    private static final String AJAX_KEY_ONERROR = "onerror";

-    private static final String AJAX_KEY_ONEVENT = "onevent";

-    private static final String AJAX_KEY_EXECUTE = "execute";

-    private static final String AJAX_KEY_RENDER = "render";

-    private static final String AJAX_KEY_RESETVALUES = "resetValues";

-

-    private static final String AJAX_VAL_THIS = "this";

-    private static final String JS_AJAX_REQUEST = "jsf.ajax.request";

-    

     private static final String AJAX_SB = "oam.renderkit.AJAX_SB";

-    private static final String AJAX_PARAM_SB = "oam.renderkit.AJAX_PARAM_SB";

     

     @Override

     public void encodeBegin(FacesContext context, UIComponent component) throws IOException

@@ -98,14 +83,24 @@
         script.append("var "+name+" = function(o){var o=(typeof o==='object')&&o?o:{};");

         script.prettyLine();

         

+        List<UIParameter> uiParams = HtmlRendererUtils.getValidUIParameterChildren(

+                context, getChildren(commandScript), false, false);

         

-        Collection<ClientBehaviorContext.Parameter> eventParameters = null;    

-        //eventParameters.add(new ClientBehaviorContext.Parameter("params", "o"));

-        ClientBehaviorContext ccc = ClientBehaviorContext.createClientBehaviorContext(

-                                    context, component, "action",

-                                    commandScript.getClientId(context), eventParameters);

-        

-        script.append(makeAjax(context, ccc, commandScript).toString());

+        StringBuilder ajax = SharedStringBuilder.get(context, AJAX_SB, 60);

+

+        AjaxScriptBuilder.build(context,

+                ajax,

+                commandScript,

+                commandScript.getClientId(context),

+                "action",

+                commandScript.getExecute(),

+                commandScript.getRender(),

+                commandScript.getResetValues(),

+                commandScript.getOnerror(),

+                commandScript.getOnevent(),

+                uiParams);

+

+        script.append(ajax.toString());

         script.decreaseIndent();

         script.append("}");

         

@@ -183,270 +178,4 @@
             ClientBehaviorRendererUtils.decodeClientBehaviors(facesContext, component);

         }

     }

-

-    /**

-     * builds the generic ajax call depending upon

-     * the ajax behavior parameters

-     *

-     * @param context  the Client behavior context

-     * @param behavior the behavior

-     * @param commandScript the component

-     * @return a fully working javascript with calls into jsf.js

-     */

-    private StringBuilder makeAjax(FacesContext facesContext, ClientBehaviorContext context,

-            HtmlCommandScript commandScript)

-    {

-        StringBuilder retVal = SharedStringBuilder.get(context.getFacesContext(), AJAX_SB, 60);

-        StringBuilder paramBuffer = SharedStringBuilder.get(context.getFacesContext(), AJAX_PARAM_SB, 20);

-    

-        SearchExpressionContext searchExpressionContext = SearchExpressionContext.createSearchExpressionContext(

-                context.getFacesContext(), context.getComponent(),

-                MyFacesSearchExpressionHints.SET_RESOLVE_CLIENT_SIDE_RESOLVE_SINGLE_COMPONENT, null);

-        

-        String executes = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_EXECUTE, commandScript.getExecute(),

-                searchExpressionContext);

-        String render = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_RENDER, commandScript.getRender(),

-                searchExpressionContext);

-

-        String onError = commandScript.getOnerror();

-        if (StringUtils.isNotBlank(onError))

-        {

-            paramBuffer.setLength(0);

-            paramBuffer.append(AJAX_KEY_ONERROR);

-            paramBuffer.append(':');

-            paramBuffer.append(onError);

-            onError = paramBuffer.toString();

-        }

-        else

-        {

-            onError = null;

-        }

-

-        String onEvent = commandScript.getOnevent();

-        if (StringUtils.isNotBlank(onEvent))

-        {

-            paramBuffer.setLength(0);

-            paramBuffer.append(AJAX_KEY_ONEVENT);

-            paramBuffer.append(':');

-            paramBuffer.append(onEvent);

-            onEvent = paramBuffer.toString();

-        }

-        else

-        {

-            onEvent = null;

-        }

-

-        String resetValues = null;

-        if (Boolean.TRUE.equals(commandScript.getResetValues()))

-        {

-            paramBuffer.setLength(0);

-            paramBuffer.append(AJAX_KEY_RESETVALUES);

-            paramBuffer.append(':');

-            paramBuffer.append("true");

-            resetValues = paramBuffer.toString();

-        }

-

-        String sourceId = null;

-        if (context.getSourceId() == null)

-        {

-            sourceId = AJAX_VAL_THIS;

-        }

-        else

-        {

-            paramBuffer.setLength(0);

-            paramBuffer.append('\'');

-            paramBuffer.append(context.getSourceId());

-            paramBuffer.append('\'');

-            sourceId = paramBuffer.toString();

-

-            if (!context.getSourceId().trim().equals(

-                context.getComponent().getClientId(context.getFacesContext())))

-            {

-                // Check if sourceId is not a clientId and there is no execute set

-                UIComponent ref = context.getComponent();

-                ref = (ref.getParent() == null) ? ref : ref.getParent();

-                UIComponent instance = null;

-                try

-                {

-                    instance = ref.findComponent(context.getSourceId());

-                }

-                catch (IllegalArgumentException e)

-                {

-                    // No Op

-                }

-                if (instance == null && executes == null)

-                {

-                    // set the clientId of the component so the behavior can be decoded later,

-                    // otherwise the behavior will fail

-                    executes = resolveExpressionsAsParameter(paramBuffer, AJAX_KEY_EXECUTE,

-                            context.getComponent().getClientId(context.getFacesContext()), searchExpressionContext);

-                }

-            }

-        }

-

-        String event = context.getEventName();

-

-        retVal.append(JS_AJAX_REQUEST);

-        retVal.append('(');

-        retVal.append(sourceId);

-        retVal.append(",window.event,myfaces._impl._util._Lang.mixMaps(");

-        

-        Collection<ClientBehaviorContext.Parameter> params = context.getParameters();

-        int paramSize = (params != null) ? params.size() : 0;

-

-        List<String> parameterList = new ArrayList<>(paramSize + 2);

-        if (executes != null)

-        {

-            parameterList.add(executes);

-        }

-        if (render != null)

-        {

-            parameterList.add(render);

-        }

-        if (onError != null)

-        {

-            parameterList.add(onError);

-        }

-        if (onEvent != null)

-        {

-            parameterList.add(onEvent);

-        }

-        if (resetValues != null)

-        {

-            parameterList.add(resetValues);

-        }

-        if (paramSize > 0)

-        {

-            /**

-             * see ClientBehaviorContext.html of the spec

-             * the param list has to be added in the post back

-             */

-            // params are in 99% RamdonAccess instace created in

-            // HtmlRendererUtils.getClientBehaviorContextParameters(Map<String, String>)

-            if (params instanceof RandomAccess)

-            {

-                List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;

-                for (int i = 0, size = list.size(); i < size; i++)

-                {

-                    ClientBehaviorContext.Parameter param = list.get(i);

-                    append(paramBuffer, parameterList, param.getName(), param.getValue());

-                }

-            }

-            else

-            {

-                for (ClientBehaviorContext.Parameter param : params)

-                {

-                    append(paramBuffer, parameterList, param.getName(), param.getValue());

-                }

-            }

-        }

-

-        List<UIParameter> uiParams = HtmlRendererUtils.getValidUIParameterChildren(

-                facesContext, getChildren(commandScript), false, false);

-        if (uiParams != null && uiParams.size() > 0)

-        {

-            for (int i = 0, size = uiParams.size(); i < size; i++)

-            {

-                UIParameter param = uiParams.get(i);

-                append(paramBuffer, parameterList, param.getName(), param.getValue());

-            }

-        }

-            

-        paramBuffer.setLength(0);

-        paramBuffer.append('\'');

-        paramBuffer.append(ClientBehaviorContext.BEHAVIOR_EVENT_PARAM_NAME);

-        paramBuffer.append("\':\'");

-        paramBuffer.append(event);

-        paramBuffer.append('\'');

-        parameterList.add(paramBuffer.toString());

-

-        /**

-         * I assume here for now that the options are the same which also

-         * can be sent via the options attribute to javax.faces.ajax

-         * this still needs further clarifications but I assume so for now

-         */

-        retVal.append(buildOptions(paramBuffer, parameterList));

-

-        //mixMaps

-        retVal.append(",o,false))");

-

-        return retVal;

-    }

-

-    private void append(StringBuilder paramBuffer, List<String> parameterList, String paramName, Object paramValue)

-    {

-        //Both name and value should be quoted

-        paramBuffer.setLength(0);

-        paramBuffer.append('\'');

-        paramBuffer.append(paramName);

-        paramBuffer.append("\':\'");

-        if (paramValue != null)

-        {

-            paramBuffer.append(paramValue.toString());

-        }

-        paramBuffer.append('\'');

-        parameterList.add(paramBuffer.toString());

-    }

-

-    private StringBuilder buildOptions(StringBuilder retVal, List<String> options)

-    {

-        retVal.setLength(0);

-

-        retVal.append('{');

-

-        boolean first = true;

-

-        for (int i = 0, size = options.size(); i < size; i++)

-        {

-            String option = options.get(i);

-            if (StringUtils.isNotBlank(option))

-            {

-                if (!first)

-                {

-                    retVal.append(',');

-                }

-                else

-                {

-                    first = false;

-                }

-                retVal.append(option);

-            }

-        }

-        retVal.append('}');

-        return retVal;

-    }

-

-    private String resolveExpressionsAsParameter(StringBuilder retVal, String target, String expressions,

-            SearchExpressionContext searchExpressionContext)

-    {

-        if (StringUtils.isNotBlank(expressions))

-        {

-            retVal.setLength(0);

-            retVal.append(target);

-            retVal.append(':');

-            retVal.append('\'');

-

-            SearchExpressionHandler handler =

-                    searchExpressionContext.getFacesContext().getApplication().getSearchExpressionHandler();

-            List<String> clientIds =

-                    handler.resolveClientIds(searchExpressionContext, expressions);

-            

-            if (clientIds != null && !clientIds.isEmpty())

-            {

-                for (int i = 0; i < clientIds.size(); i++)

-                {

-                    if (i > 0)

-                    {

-                        retVal.append(' ');

-                    }

-                    retVal.append(clientIds.get(i));

-                }

-            }

-            

-            retVal.append('\'');

-            return retVal.toString();

-        }

-

-        return null;

-    }

 }

diff --git a/impl/src/main/java/org/apache/myfaces/renderkit/html/base/AjaxScriptBuilder.java b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/AjaxScriptBuilder.java
new file mode 100644
index 0000000..6aba964
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/renderkit/html/base/AjaxScriptBuilder.java
@@ -0,0 +1,322 @@
+/*

+ * 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.myfaces.renderkit.html.base;

+

+import java.util.Collection;

+import java.util.List;

+import java.util.RandomAccess;

+import javax.faces.component.UIComponent;

+import javax.faces.component.UIParameter;

+import javax.faces.component.behavior.ClientBehaviorContext;

+import javax.faces.component.html.HtmlCommandScript;

+import javax.faces.component.search.SearchExpressionContext;

+import javax.faces.component.search.SearchExpressionHandler;

+import javax.faces.context.FacesContext;

+import org.apache.myfaces.component.search.MyFacesSearchExpressionHints;

+import org.apache.myfaces.util.lang.StringUtils;

+

+// CHECKSTYLE:OFF

+public class AjaxScriptBuilder

+{    

+    public static void build(FacesContext context,

+            StringBuilder sb,

+            UIComponent component,

+            String sourceId,

+            String eventName,

+            String execute,

+            String render,

+            Boolean resetValues,

+            String onerror,

+            String onevent,

+            List<UIParameter> uiParams)

+    {

+        build(context,

+                sb,

+                component,

+                sourceId,

+                eventName,

+                execute,

+                render,

+                null,

+                Boolean.TRUE.equals(resetValues) ? Boolean.TRUE.toString() : null,

+                onerror,

+                onevent,

+                null,

+                uiParams);

+    }

+    

+    public static void build(FacesContext context,

+            StringBuilder sb,

+            UIComponent component,

+            String sourceId,

+            String eventName,

+            Collection<String> executeList,

+            Collection<String> renderList,

+            String delay,

+            boolean resetValues,

+            String onerror,

+            String onevent,

+            Collection<ClientBehaviorContext.Parameter> params)

+    {

+        String execute = null;

+        if (executeList != null && !executeList.isEmpty())

+        {

+            execute = String.join(" ", executeList);

+        }

+            

+        String render = null;

+        if (renderList != null && !renderList.isEmpty())

+        {

+            render = String.join(" ", renderList);

+        }

+

+        build(context,

+                sb,

+                component,

+                sourceId,

+                eventName,

+                execute,

+                render,

+                delay,

+                resetValues ? Boolean.TRUE.toString() : null,

+                onerror,

+                onevent,

+                params,

+                null);

+    }

+    

+    public static void build(FacesContext context,

+            StringBuilder sb,

+            UIComponent component,

+            String sourceId,

+            String eventName,

+            String execute,

+            String render,

+            String delay,

+            String resetValues,

+            String onerror,

+            String onevent,

+            Collection<ClientBehaviorContext.Parameter> params,

+            List<UIParameter> uiParams)

+    {

+    // CHECKSTYLE:ON

+        HtmlCommandScript commandScript = (component instanceof HtmlCommandScript)

+                ? (HtmlCommandScript) component

+                : null;

+   

+        sb.append("myfaces.ab(");

+

+        if (sourceId == null)

+        {

+            sb.append("this");

+        }

+        else

+        {

+            sb.append("'");

+            sb.append(sourceId);

+            sb.append("'");

+

+            if (!sourceId.trim().equals(component.getClientId(context)))

+            {

+                // Check if sourceId is not a clientId and there is no execute set

+                UIComponent ref = (component.getParent() == null) ? component : component.getParent();

+                UIComponent instance = null;

+                try

+                {

+                    instance = ref.findComponent(sourceId);

+                }

+                catch (IllegalArgumentException e)

+                {

+                    // No Op

+                }

+                if (instance == null && execute == null)

+                {

+                    // set the clientId of the component so the behavior can be decoded later,

+                    // otherwise the behavior will fail

+                    execute = component.getClientId(context);

+                }

+            }

+        }

+        sb.append(",");

+

+        sb.append(commandScript == null ? "event" : "null");

+        sb.append(",'");

+

+        sb.append(eventName);

+        sb.append("',");

+        

+        SearchExpressionHandler seHandler = context.getApplication().getSearchExpressionHandler();

+        SearchExpressionContext seContext = SearchExpressionContext.createSearchExpressionContext(

+                context, component,

+                MyFacesSearchExpressionHints.SET_RESOLVE_CLIENT_SIDE_RESOLVE_SINGLE_COMPONENT, null);

+        

+        appendIds(sb, execute, seHandler, seContext);

+        sb.append(",");

+

+        appendIds(sb, render, seHandler, seContext);

+        

+        if (onevent != null || onerror != null || delay != null || resetValues != null

+                || (params != null && !params.isEmpty()) || (uiParams != null && !uiParams.isEmpty()))

+        {

+            sb.append(",{");

+            if (onevent != null)

+            {

+                appendProperty(sb, "onevent", onevent, false);

+            }

+            if (onerror != null)

+            {

+                appendProperty(sb, "onerror", onerror, false);

+            }

+            if (delay != null)

+            {

+                appendProperty(sb, "delay", delay, true);

+            }

+            if (resetValues != null)

+            {

+                appendProperty(sb, "resetValues", resetValues, false);

+            }

+            if (params != null && !params.isEmpty())

+            {

+                if (commandScript != null)

+                {

+                    appendProperty(sb, "params", params.iterator().next().getName(), false);

+                }

+                else

+                {

+                    appendProperty(sb, "params", "{", false);

+

+                    if (params instanceof RandomAccess)

+                    {

+                        List<ClientBehaviorContext.Parameter> list = (List<ClientBehaviorContext.Parameter>) params;

+                        for (int i = 0, size = list.size(); i < size; i++)

+                        {

+                            ClientBehaviorContext.Parameter param = list.get(i);

+                            appendProperty(sb, param.getName(), param.getValue(), true);

+                        }

+                    }

+                    else

+                    {

+                        for (ClientBehaviorContext.Parameter param : params)

+                        {

+                            appendProperty(sb, param.getName(), param.getValue(), true);

+                        }

+                    }

+

+                    sb.append("}");

+                }

+            }

+            if (uiParams != null && uiParams.size() > 0)

+            {

+                for (int i = 0, size = uiParams.size(); i < size; i++)

+                {

+                    UIParameter param = uiParams.get(i);

+                    appendProperty(sb, param.getName(), param.getValue(), true);

+                }

+            }

+

+            sb.append("}");

+        }

+

+        sb.append(")");

+    }

+    

+    private static void appendIds(StringBuilder sb, String expressions,

+            SearchExpressionHandler handler, SearchExpressionContext searchExpressionContext)

+    {

+        sb.append('\'');

+        

+        if (StringUtils.isNotBlank(expressions))

+        {

+            List<String> clientIds =

+                    handler.resolveClientIds(searchExpressionContext, expressions);

+

+            if (clientIds != null && !clientIds.isEmpty())

+            {

+                for (int i = 0; i < clientIds.size(); i++)

+                {

+                    if (i > 0)

+                    {

+                        sb.append(' ');

+                    }

+                    sb.append(clientIds.get(i));

+                }

+            }

+        }

+        

+        sb.append('\'');

+    }

+

+    // Appends an name/value property pair to a JSON object.  Assumes

+    // object has already been opened by the caller.

+    public static void appendProperty(StringBuilder builder, 

+                                      String name,

+                                      Object value,

+                                      boolean quoteValue)

+    {

+        if ((null == name) || (name.length() == 0))

+        {

+            throw new IllegalArgumentException();

+        }

+

+        char lastChar = builder.charAt(builder.length() - 1);

+        if ((lastChar != ',') && (lastChar != '{'))

+        {

+            builder.append(',');

+        }

+

+        appendQuotedValue(builder, name);

+        builder.append(":");

+

+        if (value == null)

+        {

+            builder.append("''");

+        }

+        else if (quoteValue)

+        {

+            appendQuotedValue(builder, value.toString());

+        }

+        else

+        {

+            builder.append(value.toString());

+        }

+    }

+    

+    // Append a script to the chain, escaping any single quotes, since

+    // our script content is itself nested within single quotes.

+    public static void appendQuotedValue(StringBuilder builder, String script)

+    {

+        builder.append("'");

+

+        int length = script.length();

+

+        for (int i = 0; i < length; i++)

+        {

+            char c = script.charAt(i);

+

+            if (c == '\'' || c == '\\')

+            {

+                builder.append('\\');

+            } 

+

+            builder.append(c);

+        }

+

+        builder.append("'");

+    }

+}