You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by hn...@apache.org on 2018/03/13 18:31:26 UTC

[myfaces-tobago] branch master updated: TOBAGO-1852 Sheet row to open a popup does not work

This is an automated email from the ASF dual-hosted git repository.

hnoeth pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git


The following commit(s) were added to refs/heads/master by this push:
     new fa9d600  TOBAGO-1852 Sheet row to open a popup does not work
fa9d600 is described below

commit fa9d600c94068b46d3508e88065bd100fb80effd
Author: Henning Noeth <hn...@apache.org>
AuthorDate: Tue Mar 13 19:25:34 2018 +0100

    TOBAGO-1852 Sheet row to open a popup does not work
    
    * <tc:event> can now have children of type <f:ajax> and <tc:operation>
    * Fix a NPE for merging command maps
    * Fix log message when evaluateClientId
    * EventBehavior now have an id to identify related <tc:event> component
    * 'disabled' attribute of <tc:event> is now supported - does the same as rendered
    * A component can now have both <tc:event> and <f:ajax> if only one is enabled
    * Better description for the <tc:event> tag.
    * Improve demo for behavior
    * Add test for behavior.xhtml and sheet-event.xhtml
---
 .../myfaces/tobago/facelets/EventHandler.java      |  44 ++--
 .../tobago/internal/behavior/EventBehavior.java    |   9 +
 .../tobago/internal/component/AbstractUIEvent.java |   4 +-
 .../tobago/internal/renderkit/CommandMap.java      |  18 +-
 .../internal/renderkit/renderer/RowRenderer.java   |   6 +-
 .../renderer/TobagoClientBehaviorRenderer.java     |  75 ++++--
 .../taglib/component/EventTagDeclaration.java      |  20 ++
 .../myfaces/tobago/internal/util/RenderUtils.java  |  93 +++++--
 .../apache/myfaces/tobago/util/ComponentUtils.java |   2 +-
 .../tobago/example/demo/BehaviorController.java    |  19 +-
 .../example/demo/BehaviorTestController.java       | 213 +++++++++++++++
 .../080-sheet/30-event/sheet-event.test.js         | 109 ++++++++
 .../080-sheet/30-event/sheet-event.xhtml           |  10 +-
 .../content/30-concept/30-behavior/behavior.xhtml  |  46 +++-
 .../content/40-test/6500-behavior/behavior.test.js | 292 +++++++++++++++++++++
 .../content/40-test/6500-behavior/behavior.xhtml   | 205 +++++++++++++++
 .../myfaces/tobago/example/demo/QUnitTests.java    |   6 +
 17 files changed, 1063 insertions(+), 108 deletions(-)

diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/EventHandler.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/EventHandler.java
index f6c5c41..9e6cb3a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/EventHandler.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/EventHandler.java
@@ -23,12 +23,11 @@ import org.apache.myfaces.tobago.component.Attributes;
 import org.apache.myfaces.tobago.component.ClientBehaviors;
 import org.apache.myfaces.tobago.component.UIEvent;
 import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
+import org.apache.myfaces.tobago.internal.component.AbstractUIEvent;
 
 import javax.el.MethodExpression;
 import javax.faces.component.PartialStateHolder;
 import javax.faces.component.UIComponent;
-import javax.faces.component.behavior.AjaxBehavior;
-import javax.faces.component.behavior.ClientBehavior;
 import javax.faces.component.behavior.ClientBehaviorHolder;
 import javax.faces.context.FacesContext;
 import javax.faces.event.AbortProcessingException;
@@ -42,8 +41,6 @@ import javax.faces.view.facelets.TagAttribute;
 import javax.faces.view.facelets.TagAttributeException;
 import javax.faces.view.facelets.TagException;
 import java.io.IOException;
-import java.util.List;
-import java.util.Map;
 
 /**
  * This tag creates an instance of AjaxBehavior, and associates it with the nearest
@@ -141,37 +138,26 @@ public class EventHandler extends TobagoComponentHandler implements BehaviorHold
     final FaceletContext faceletContext = (FaceletContext) context
         .getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY);
 
-    // cast to a ClientBehaviorHolder
-    final ClientBehaviorHolder cvh = (ClientBehaviorHolder) parent;
+    final ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) parent;
+    final UIComponent lastChild = parent.getChildren().stream().skip(parent.getChildCount() - 1).findFirst().orElse(null);
+    final AbstractUIEvent abstractUIEvent = lastChild instanceof AbstractUIEvent ? (AbstractUIEvent) lastChild : null;
 
-    String eventName = getEventName();
-    if (eventName == null) {
-      eventName = cvh.getDefaultEventName();
+    if (abstractUIEvent != null) {
+      String eventName = getEventName();
       if (eventName == null) {
-        throw new TagAttributeException(event, "eventName could not be defined for f:ajax tag with no wrap mode.");
-      }
-    } else if (!cvh.getEventNames().contains(eventName)) {
-      throw new TagAttributeException(event, "event it is not a valid eventName defined for this component");
-    }
-
-    final Map<String, List<ClientBehavior>> clientBehaviors = cvh.getClientBehaviors();
-
-    final List<ClientBehavior> clientBehaviorList = clientBehaviors.get(eventName);
-    if (clientBehaviorList != null && !clientBehaviorList.isEmpty()) {
-      for (final ClientBehavior cb : clientBehaviorList) {
-        if (cb instanceof AjaxBehavior) {
-          // The most inner one has been applied, so according to
-          // jsf 2.0 spec section 10.4.1.1 it is not necessary to apply
-          // this one, because the inner one has precendece over
-          // the outer one.
-          return;
+        eventName = clientBehaviorHolder.getDefaultEventName();
+        if (eventName == null) {
+          throw new TagAttributeException(event, "eventName could not be defined for f:ajax tag with no wrap mode.");
         }
+      } else if (!clientBehaviorHolder.getEventNames().contains(eventName)) {
+        throw new TagAttributeException(event, "event it is not a valid eventName defined for this component");
       }
-    }
 
-    final EventBehavior ajaxBehavior = createBehavior(context);
+      final EventBehavior eventBehavior = createBehavior(context);
+      eventBehavior.setId(abstractUIEvent.getId());
 
-    cvh.addClientBehavior(eventName, ajaxBehavior);
+      clientBehaviorHolder.addClientBehavior(eventName, eventBehavior);
+    }
   }
 
   protected EventBehavior createBehavior(final FacesContext context) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/behavior/EventBehavior.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/behavior/EventBehavior.java
index a112688..8b342d3 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/behavior/EventBehavior.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/behavior/EventBehavior.java
@@ -56,6 +56,7 @@ public class EventBehavior extends ClientBehaviorBase {
   private static final String ATTR_RENDER = "render";
   private static final String ATTR_DISABLED = "disabled";
   private static final String ATTR_IMMEDIATE = "immediate";
+  private static final String ATTR_ID = "id";
 
   /**
    * special render and execute targets
@@ -189,6 +190,14 @@ public class EventBehavior extends ClientBehaviorBase {
     return (getStateHelper().get(ATTR_IMMEDIATE) != null) || (getValueExpression(ATTR_IMMEDIATE) != null);
   }
 
+  public String getId() {
+    return (String) getStateHelper().eval(ATTR_ID);
+  }
+
+  public void setId(final String clientId) {
+    getStateHelper().put(ATTR_ID, clientId);
+  }
+
   @Override
   public Set<ClientBehaviorHint> getHints() {
     return EnumSet.of(ClientBehaviorHint.SUBMITTING);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIEvent.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIEvent.java
index ee71ad2..6265e85 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIEvent.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIEvent.java
@@ -21,10 +21,12 @@ package org.apache.myfaces.tobago.internal.component;
 
 import org.apache.myfaces.tobago.component.ClientBehaviors;
 
+import javax.faces.component.behavior.ClientBehaviorHolder;
+
 /**
  * {@link org.apache.myfaces.tobago.internal.taglib.component.EventTagDeclaration}
  */
-public abstract class AbstractUIEvent extends AbstractUICommandBase {
+public abstract class AbstractUIEvent extends AbstractUICommandBase implements ClientBehaviorHolder {
 
   public abstract ClientBehaviors getEvent();
 
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/CommandMap.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/CommandMap.java
index 8c82b08..b2816f0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/CommandMap.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/CommandMap.java
@@ -104,14 +104,16 @@ public class CommandMap {
             c1.merge(c2);
           }
         } else {
-          for (final Map.Entry<ClientBehaviors, Command> entry : m2.getOther().entrySet()) {
-            final ClientBehaviors key = entry.getKey();
-            final Command value = entry.getValue();
-            if (m1.other.containsKey(key)) {
-              final Command command = m1.other.get(key);
-              command.merge(value);
-            } else {
-              m1.addCommand(key, value);
+          if (m1.other != null && m2.getOther() != null) {
+            for (final Map.Entry<ClientBehaviors, Command> entry : m2.getOther().entrySet()) {
+              final ClientBehaviors key = entry.getKey();
+              final Command value = entry.getValue();
+              if (m1.other.containsKey(key)) {
+                final Command command = m1.other.get(key);
+                command.merge(value);
+              } else {
+                m1.addCommand(key, value);
+              }
             }
           }
         }
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/RowRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/RowRenderer.java
index 9c1bc12..209e05a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/RowRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/RowRenderer.java
@@ -33,7 +33,11 @@ public class RowRenderer extends DecodingCommandRendererBase {
     AbstractUIEvent event = null;
     for (final UIComponent uiComponent : component.getChildren()) {
       if (uiComponent instanceof AbstractUIEvent) {
-        event = (AbstractUIEvent) uiComponent;
+        AbstractUIEvent abstractUIEvent = (AbstractUIEvent) uiComponent;
+        if (abstractUIEvent.isRendered() && !abstractUIEvent.isDisabled()) {
+          event = (AbstractUIEvent) uiComponent;
+          break;
+        }
       }
     }
     if (event != null) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java
index c3b8130..e5d68e6 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TobagoClientBehaviorRenderer.java
@@ -53,6 +53,7 @@ public class TobagoClientBehaviorRenderer extends javax.faces.render.ClientBehav
   /**
    * In standard JSF this method returns a JavaScript string. Because of CSP, Tobago doesn't render JavaScript
    * into the HTML content. It transports the return value a bit hacky by {@link CommandMap#storeCommandMap}.
+   *
    * @return "dummy" string or null, if nothing to do.
    */
   @Override
@@ -63,7 +64,7 @@ public class TobagoClientBehaviorRenderer extends javax.faces.render.ClientBehav
     final ClientBehaviors eventName = ClientBehaviors.valueOf(behaviorContext.getEventName());
 
     //// TBD: is this nice? May be implemented with a JSF behaviour?
-    final Collapse collapse = createCollapsible(facesContext, uiComponent);
+    Collapse collapse = createCollapsible(facesContext, uiComponent);
 
     String executeIds = null;
     String renderIds = null;
@@ -73,7 +74,7 @@ public class TobagoClientBehaviorRenderer extends javax.faces.render.ClientBehav
     boolean omit = false;
     if (behavior instanceof AjaxBehavior) {
       final AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
-      if (ajaxBehavior.isDisabled()){
+      if (ajaxBehavior.isDisabled()) {
         return null;
       }
       final Collection<String> execute = ajaxBehavior.getExecute();
@@ -97,14 +98,19 @@ public class TobagoClientBehaviorRenderer extends javax.faces.render.ClientBehav
           ComponentUtils.evaluateClientIds(facesContext, uiComponent, render.toArray(new String[render.size()]));
       actionId = clientId;
     } else if (behavior instanceof EventBehavior) { // <tc:event>
-      final AbstractUIEvent event = findEvent(uiComponent, eventName);
+      final EventBehavior eventBehavior = (EventBehavior) behavior;
+      final AbstractUIEvent event = RenderUtils.getAbstractUIEvent(uiComponent, eventBehavior);
+
       if (event != null) {
-        if (!event.isRendered()) {
+        if (!event.isRendered() || event.isDisabled()) {
           return null;
         } else {
           transition = event.isTransition();
           target = event.getTarget();
           actionId = event.getClientId(facesContext);
+          if (collapse == null) {
+            collapse = createCollapsible(facesContext, event);
+          }
           omit = event.isOmit() || StringUtils.isNotBlank(RenderUtils.generateUrl(facesContext, event));
         }
       }
@@ -132,39 +138,52 @@ public class TobagoClientBehaviorRenderer extends javax.faces.render.ClientBehav
     return "dummy";
   }
 
-  private AbstractUIEvent findEvent(final UIComponent component, final ClientBehaviors eventName) {
-    for (final UIComponent child : component.getChildren()) {
-      if (child instanceof AbstractUIEvent) {
-        final AbstractUIEvent event = (AbstractUIEvent) child;
-        if (eventName == event.getEvent()) {
-          return event;
-        }
+  @Override
+  public void decode(final FacesContext facesContext, final UIComponent component,
+      final ClientBehavior clientBehavior) {
+    if (clientBehavior instanceof AjaxBehavior) {
+      AjaxBehavior ajaxBehavior = (AjaxBehavior) clientBehavior;
+      if (!component.isRendered() || ajaxBehavior.isDisabled()) {
+        return;
       }
-    }
-    return null;
-  }
 
-  @Override
-  public void decode(final FacesContext context, final UIComponent component, final ClientBehavior behavior) {
-    final AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
-    if (ajaxBehavior.isDisabled() || !component.isRendered()) {
-      return;
-    }
+      dispatchBehaviorEvent(component, ajaxBehavior);
+    } else if (clientBehavior instanceof EventBehavior) {
+      final EventBehavior eventBehavior = (EventBehavior) clientBehavior;
+      final AbstractUIEvent abstractUIEvent = RenderUtils.getAbstractUIEvent(component, eventBehavior);
 
-    dispatchBehaviorEvent(component, ajaxBehavior);
-  }
+      if (!component.isRendered() || abstractUIEvent == null
+          || !abstractUIEvent.isRendered() || abstractUIEvent.isDisabled()) {
+        return;
+      }
 
-  private void dispatchBehaviorEvent(final UIComponent component, final AjaxBehavior ajaxBehavior) {
+      for (List<ClientBehavior> children : abstractUIEvent.getClientBehaviors().values()) {
+        for (ClientBehavior child : children) {
+          decode(facesContext, component, child);
+        }
+      }
+      dispatchBehaviorEvent(component, eventBehavior);
+    }
+  }
 
-    final AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, ajaxBehavior);
-    final boolean isImmediate = isImmediate(ajaxBehavior, component);
+  private void dispatchBehaviorEvent(final UIComponent component, final ClientBehavior clientBehavior) {
+    final AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, clientBehavior);
+    final boolean isImmediate = isImmediate(clientBehavior, component);
     event.setPhaseId(isImmediate ? PhaseId.APPLY_REQUEST_VALUES : PhaseId.INVOKE_APPLICATION);
     component.queueEvent(event);
   }
 
-  private boolean isImmediate(final AjaxBehavior ajaxBehavior, final UIComponent component) {
-    if (ajaxBehavior.isImmediateSet()) {
-      return ajaxBehavior.isImmediate();
+  private boolean isImmediate(final ClientBehavior clientBehavior, final UIComponent component) {
+    if (clientBehavior instanceof AjaxBehavior) {
+      AjaxBehavior ajaxBehavior = (AjaxBehavior) clientBehavior;
+      if (ajaxBehavior.isImmediateSet()) {
+        return ajaxBehavior.isImmediate();
+      }
+    } else if (clientBehavior instanceof EventBehavior) {
+      EventBehavior eventBehavior = (EventBehavior) clientBehavior;
+      if (eventBehavior.isImmediateSet()) {
+        return eventBehavior.isImmediate();
+      }
     }
     if (component instanceof EditableValueHolder) {
       return ((EditableValueHolder) component).isImmediate();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/EventTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/EventTagDeclaration.java
index 4d69cc6..abb6466 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/EventTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/EventTagDeclaration.java
@@ -19,10 +19,12 @@
 
 package org.apache.myfaces.tobago.internal.taglib.component;
 
+import org.apache.myfaces.tobago.apt.annotation.Behavior;
 import org.apache.myfaces.tobago.apt.annotation.Tag;
 import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
 import org.apache.myfaces.tobago.apt.annotation.UIComponentTag;
 import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.component.ClientBehaviors;
 import org.apache.myfaces.tobago.component.RendererTypes;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasAction;
 import org.apache.myfaces.tobago.internal.taglib.declaration.HasActionListener;
@@ -42,6 +44,7 @@ import javax.faces.component.UICommand;
 
 /**
  * Add an event behavior to the component.
+ * It can contain f:ajax and tc:operation tags.
  */
 @Tag(name = "event")
 @UIComponentTag(uiComponent = "org.apache.myfaces.tobago.component.UIEvent",
@@ -53,6 +56,22 @@ import javax.faces.component.UICommand;
         // As long as no behavior event names are defined, ClientBehaviorHolder must be implemented for Majorra.
         "javax.faces.component.behavior.ClientBehaviorHolder"
     },
+    behaviors = {
+        @Behavior(name = ClientBehaviors.CHANGE),
+        @Behavior(
+            name = ClientBehaviors.CLICK,
+            description = "Behavior of a click event.",
+            isDefault = true),
+        @Behavior(name = ClientBehaviors.DBLCLICK),
+        @Behavior(name = ClientBehaviors.FOCUS),
+        @Behavior(name = ClientBehaviors.BLUR),
+        @Behavior(name = ClientBehaviors.MOUSEOUT),
+        @Behavior(name = ClientBehaviors.MOUSEOVER),
+        @Behavior(name = ClientBehaviors.COMPLETE),
+        @Behavior(name = ClientBehaviors.LOAD),
+        @Behavior(name = ClientBehaviors.RELOAD),
+        @Behavior(name = ClientBehaviors.RESIZE)
+    },
     faceletHandler = "org.apache.myfaces.tobago.facelets.EventHandler")
 public interface EventTagDeclaration
     extends HasIdBindingAndRendered, HasAction, HasActionListener, IsImmediateCommand, HasConfirmation,
@@ -60,6 +79,7 @@ public interface EventTagDeclaration
 
   /**
    * The name of the event as an instance of {@link org.apache.myfaces.tobago.component.ClientBehaviors}
+   * This will be also overwrite events of possible f:ajax children.
    */
   @TagAttribute
   @UIComponentTagAttribute(type = "org.apache.myfaces.tobago.component.ClientBehaviors")
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java
index 36db432..50c6efe 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/RenderUtils.java
@@ -20,9 +20,11 @@
 package org.apache.myfaces.tobago.internal.util;
 
 import org.apache.myfaces.tobago.component.ClientBehaviors;
+import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
 import org.apache.myfaces.tobago.internal.component.AbstractUICommandBase;
 import org.apache.myfaces.tobago.internal.component.AbstractUIData;
+import org.apache.myfaces.tobago.internal.component.AbstractUIEvent;
 import org.apache.myfaces.tobago.internal.component.AbstractUITreeNodeBase;
 import org.apache.myfaces.tobago.internal.renderkit.Command;
 import org.apache.myfaces.tobago.internal.renderkit.CommandMap;
@@ -51,6 +53,7 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 public final class RenderUtils {
 
@@ -289,41 +292,79 @@ public final class RenderUtils {
     return url;
   }
 
-  public static CommandMap getBehaviorCommands(final FacesContext facesContext, final ClientBehaviorHolder holder) {
-    CommandMap map = null;
-    final Map<String, List<ClientBehavior>> behaviors = holder.getClientBehaviors();
-    for (final Map.Entry<String, List<ClientBehavior>> behaviorMap : behaviors.entrySet()) {
-      final String key = behaviorMap.getKey();
-      final ClientBehaviorContext context = ClientBehaviorContext.createClientBehaviorContext(
-          facesContext, (UIComponent) holder, key, ((UIComponent) holder).getClientId(facesContext), null);
-      for (final ClientBehavior clientBehavior : behaviorMap.getValue()) {
-        if (clientBehavior instanceof ClientBehaviorBase) {
-          String type = ((ClientBehaviorBase) clientBehavior).getRendererType();
-          // XXX this is to use a different renderer for Tobago components and other components.
-          if (type.equals(AjaxBehavior.BEHAVIOR_ID)) {
-            type = "org.apache.myfaces.tobago.behavior.Ajax";
-          }
-          final ClientBehaviorRenderer renderer = facesContext.getRenderKit().getClientBehaviorRenderer(type);
-          final String dummy = renderer.getScript(context, clientBehavior);
-          if (dummy != null) {
-            map = CommandMap.merge(map, CommandMap.restoreCommandMap(facesContext));
+  public static CommandMap getBehaviorCommands(final FacesContext facesContext,
+      final ClientBehaviorHolder clientBehaviorHolder) {
+    CommandMap commandMap = null;
+
+    for (final Map.Entry<String, List<ClientBehavior>> entry : clientBehaviorHolder.getClientBehaviors().entrySet()) {
+      final String eventName = entry.getKey();
+      final ClientBehaviorContext clientBehaviorContext
+          = getClientBehaviorContext(facesContext, clientBehaviorHolder, eventName);
+
+      for (final ClientBehavior clientBehavior : entry.getValue()) {
+        if (clientBehavior instanceof EventBehavior) {
+          final EventBehavior eventBehavior = (EventBehavior) clientBehavior;
+          final AbstractUIEvent abstractUIEvent = getAbstractUIEvent((UIComponent) clientBehaviorHolder, eventBehavior);
+
+          if (abstractUIEvent != null && abstractUIEvent.isRendered() && !abstractUIEvent.isDisabled()) {
+            for (List<ClientBehavior> children : abstractUIEvent.getClientBehaviors().values()) {
+              for (ClientBehavior child : children) {
+                final CommandMap childMap = getCommandMap(facesContext, clientBehaviorContext, child);
+                commandMap = CommandMap.merge(commandMap, childMap);
+              }
+            }
           }
-        } else {
-          LOG.warn("Ignoring: '{}'", clientBehavior);
         }
+
+        final CommandMap map = getCommandMap(facesContext, clientBehaviorContext, clientBehavior);
+        commandMap = CommandMap.merge(commandMap, map);
       }
     }
 
     // if there is no explicit behavior (with f:ajax or tc:event), use the command properties as default.
-    // tbd: think about refactoring: put this into TobagoClientBehaviorRenderer
-    if ((map == null || map.isEmpty()) && holder instanceof AbstractUICommand) {
-      if (map == null) {
-        map = new CommandMap();
+    if ((commandMap == null || commandMap.isEmpty()) && clientBehaviorHolder instanceof AbstractUICommand) {
+      if (commandMap == null) {
+        commandMap = new CommandMap();
       }
-      map.addCommand(ClientBehaviors.click, new Command(facesContext, (AbstractUICommand) holder));
+      commandMap.addCommand(ClientBehaviors.click, new Command(facesContext, (AbstractUICommand) clientBehaviorHolder));
     }
 
-    return map;
+    return commandMap;
+  }
+
+  private static ClientBehaviorContext getClientBehaviorContext(final FacesContext facesContext,
+      final ClientBehaviorHolder clientBehaviorHolder, final String eventName) {
+    UIComponent component = (UIComponent) clientBehaviorHolder;
+    return ClientBehaviorContext.createClientBehaviorContext(facesContext, component, eventName,
+        component.getClientId(facesContext), null);
+  }
+
+  public static AbstractUIEvent getAbstractUIEvent(final UIComponent parent,
+      final EventBehavior eventBehavior) {
+    return (AbstractUIEvent) parent.getChildren().stream()
+        .filter(child -> child instanceof AbstractUIEvent)
+        .filter(child -> Objects.equals(child.getId(), eventBehavior.getId()))
+        .findFirst().orElse(null);
+  }
+
+  private static CommandMap getCommandMap(final FacesContext facesContext,
+      final ClientBehaviorContext clientBehaviorContext, final ClientBehavior clientBehavior) {
+    if (clientBehavior instanceof ClientBehaviorBase) {
+      String type = ((ClientBehaviorBase) clientBehavior).getRendererType();
+
+      // this is to use a different renderer for Tobago components and other components.
+      if (type.equals(AjaxBehavior.BEHAVIOR_ID)) {
+        type = "org.apache.myfaces.tobago.behavior.Ajax";
+      }
+      final ClientBehaviorRenderer renderer = facesContext.getRenderKit().getClientBehaviorRenderer(type);
+      final String dummy = renderer.getScript(clientBehaviorContext, clientBehavior);
+      if (dummy != null) {
+        return CommandMap.restoreCommandMap(facesContext);
+      }
+    } else {
+      LOG.warn("Ignoring: '{}'", clientBehavior);
+    }
+    return null;
   }
 
   public static void decodeClientBehaviors(final FacesContext facesContext, final UIComponent component) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
index ad83ddb..c9045b1 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
@@ -692,7 +692,7 @@ public final class ComponentUtils {
       return clientId;
     }
     LOG.error("No component found for id='{}', search base component is '{}'",
-        component != null ? component.getClientId(context) : "<null>");
+        componentId, component != null ? component.getClientId(context) : "<null>");
     return null;
   }
 
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorController.java
index 51bcfd4..9942530 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorController.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorController.java
@@ -19,16 +19,19 @@
 
 package org.apache.myfaces.tobago.example.demo;
 
-import javax.enterprise.context.RequestScoped;
+import javax.enterprise.context.SessionScoped;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.AjaxBehaviorEvent;
 import javax.inject.Named;
 import java.io.Serializable;
 
-@RequestScoped
+@SessionScoped
 @Named
 public class BehaviorController implements Serializable {
 
   private String ajax;
   private String event;
+  private int counter;
 
   public String getAjax() {
     return ajax;
@@ -45,4 +48,16 @@ public class BehaviorController implements Serializable {
   public void setEvent(final String event) {
     this.event = event;
   }
+
+  public int getCounter() {
+    return counter;
+  }
+
+  public void countUp(final AjaxBehaviorEvent event) {
+    counter++;
+  }
+
+  public void countUp(final ActionEvent event) {
+    counter++;
+  }
 }
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorTestController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorTestController.java
new file mode 100644
index 0000000..42ed101
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/BehaviorTestController.java
@@ -0,0 +1,213 @@
+/*
+ * 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.tobago.example.demo;
+
+import javax.enterprise.context.SessionScoped;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.AjaxBehaviorEvent;
+import javax.inject.Named;
+import java.io.Serializable;
+
+@SessionScoped
+@Named
+public class BehaviorTestController implements Serializable {
+
+  private int buttonActionCounter;
+  private int buttonActionListenerCounter;
+  private int actionCounter1;
+  private int actionListenerCounter1;
+  private int ajaxListenerCounter1;
+  private int actionCounter2;
+  private int actionListenerCounter2;
+  private int ajaxListenerCounter2;
+  private int actionCounter3;
+  private int actionListenerCounter3;
+  private int ajaxListenerCounter3;
+  private int selector;
+  private boolean eventEnabled1;
+  private boolean eventEnabled2;
+  private boolean eventEnabled3;
+  private boolean ajaxEnabled1;
+  private boolean ajaxEnabled2;
+  private boolean ajaxEnabled3;
+
+  public BehaviorTestController() {
+    reset();
+  }
+
+  public void reset() {
+    buttonActionCounter = 0;
+    buttonActionListenerCounter = 0;
+    actionCounter1 = 0;
+    actionListenerCounter1 = 0;
+    ajaxListenerCounter1 = 0;
+    actionCounter2 = 0;
+    actionListenerCounter2 = 0;
+    ajaxListenerCounter2 = 0;
+    actionCounter3 = 0;
+    actionListenerCounter3 = 0;
+    ajaxListenerCounter3 = 0;
+  }
+
+  public void countButtonAction() {
+    buttonActionCounter++;
+  }
+
+  public void countButtonActionListener(ActionEvent actionEvent) {
+    buttonActionListenerCounter++;
+  }
+
+  public void countAction1() {
+    actionCounter1++;
+  }
+
+  public void countActionListener1(ActionEvent actionEvent) {
+    actionListenerCounter1++;
+  }
+
+  public void countAjaxListener1(AjaxBehaviorEvent ajaxBehaviorEvent) {
+    ajaxListenerCounter1++;
+  }
+
+  public void countAction2() {
+    actionCounter2++;
+  }
+
+  public void countActionListener2(ActionEvent actionEvent) {
+    actionListenerCounter2++;
+  }
+
+  public void countAjaxListener2(AjaxBehaviorEvent ajaxBehaviorEvent) {
+    ajaxListenerCounter2++;
+  }
+
+  public void countAction3() {
+    actionCounter3++;
+  }
+
+  public void countActionListener3(ActionEvent actionEvent) {
+    actionListenerCounter3++;
+  }
+
+  public void countAjaxListener3(AjaxBehaviorEvent ajaxBehaviorEvent) {
+    ajaxListenerCounter3++;
+  }
+
+  public int getSelector() {
+    return selector;
+  }
+
+  public void setSelector(int selector) {
+    this.selector = selector;
+  }
+
+  public void submitSelection() {
+    eventEnabled1 = false;
+    eventEnabled2 = false;
+    eventEnabled3 = false;
+    ajaxEnabled1 = false;
+    ajaxEnabled2 = false;
+    ajaxEnabled3 = false;
+
+    switch (selector) {
+      case 1:
+        eventEnabled1 = true;
+        break;
+      case 2:
+        eventEnabled2 = true;
+        ajaxEnabled3 = true;
+        break;
+      case 3:
+        eventEnabled3 = true;
+        ajaxEnabled1 = true;
+        ajaxEnabled2 = true;
+        ajaxEnabled3 = true;
+        break;
+    }
+  }
+
+  public int getButtonActionCounter() {
+    return buttonActionCounter;
+  }
+
+  public int getButtonActionListenerCounter() {
+    return buttonActionListenerCounter;
+  }
+
+  public int getActionCounter1() {
+    return actionCounter1;
+  }
+
+  public int getActionListenerCounter1() {
+    return actionListenerCounter1;
+  }
+
+  public int getAjaxListenerCounter1() {
+    return ajaxListenerCounter1;
+  }
+
+  public int getActionCounter2() {
+    return actionCounter2;
+  }
+
+  public int getActionListenerCounter2() {
+    return actionListenerCounter2;
+  }
+
+  public int getAjaxListenerCounter2() {
+    return ajaxListenerCounter2;
+  }
+
+  public int getActionCounter3() {
+    return actionCounter3;
+  }
+
+  public int getActionListenerCounter3() {
+    return actionListenerCounter3;
+  }
+
+  public int getAjaxListenerCounter3() {
+    return ajaxListenerCounter3;
+  }
+
+  public boolean isEventEnabled1() {
+    return eventEnabled1;
+  }
+
+  public boolean isEventEnabled2() {
+    return eventEnabled2;
+  }
+
+  public boolean isEventEnabled3() {
+    return eventEnabled3;
+  }
+
+  public boolean isAjaxEnabled1() {
+    return ajaxEnabled1;
+  }
+
+  public boolean isAjaxEnabled2() {
+    return ajaxEnabled2;
+  }
+
+  public boolean isAjaxEnabled3() {
+    return ajaxEnabled3;
+  }
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.test.js
index 3ab16ad..2194cb5 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.test.js
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.test.js
@@ -201,3 +201,112 @@ QUnit.test("On double click with full request", function (assert) {
     }
   });
 });
+
+QUnit.test("Open popup on click with ajax", function (assert) {
+  assert.expect(12);
+  var done = assert.async(7);
+
+  var $radioButton = jQueryFrame("#page\\:mainForm\\:changeExample\\:\\:3");
+  var $venus = jQueryFrame("#page\\:mainForm\\:s1\\:2\\:sample3");
+  var $jupiter = jQueryFrame("#page\\:mainForm\\:s1\\:5\\:sample3");
+  var $saturn = jQueryFrame("#page\\:mainForm\\:s1\\:6\\:sample3");
+  var $popup = jQueryFrame("#page\\:mainForm\\:popup");
+  var $name = jQueryFrame("#page\\:mainForm\\:popup\\:popupName\\:\\:field");
+  var $cancel = jQueryFrame("#page\\:mainForm\\:popup\\:cancel");
+
+  $radioButton.click();
+
+  waitForAjax(function () {
+    $venus = jQueryFrame($venus.selector);
+    $jupiter = jQueryFrame($jupiter.selector);
+    $saturn = jQueryFrame($saturn.selector);
+    return $venus.length === 1 && $jupiter.length === 1 && $saturn.length === 1;
+  }, function () {
+    $venus = jQueryFrame($venus.selector);
+    $jupiter = jQueryFrame($jupiter.selector);
+    $saturn = jQueryFrame($saturn.selector);
+    assert.equal($venus.length, 1);
+    assert.equal($jupiter.length, 1);
+    assert.equal($saturn.length, 1);
+    done();
+
+    $venus.click();
+
+    waitForAjax(function () {
+      $popup = jQueryFrame($popup.selector);
+      $name = jQueryFrame($name.selector);
+      return $popup.hasClass("show") && $name.val() === "Venus";
+    }, function () {
+      $popup = jQueryFrame($popup.selector);
+      $name = jQueryFrame($name.selector);
+      assert.ok($popup.hasClass("show"));
+      assert.equal($name.val(), "Venus");
+      done();
+
+      waitForAjax(function () {
+        $cancel = jQueryFrame($cancel.selector);
+        $popup = jQueryFrame($popup.selector);
+
+        $cancel.click();
+        return !$popup.hasClass("show");
+      }, function () {
+        $popup = jQueryFrame($popup.selector);
+        assert.notOk($popup.hasClass("show"));
+        done();
+
+        $jupiter.click();
+
+        waitForAjax(function () {
+          $popup = jQueryFrame($popup.selector);
+          $name = jQueryFrame($name.selector);
+          return $popup.hasClass("show") && $name.val() === "Jupiter";
+        }, function () {
+          $popup = jQueryFrame($popup.selector);
+          $name = jQueryFrame($name.selector);
+          assert.ok($popup.hasClass("show"));
+          assert.equal($name.val(), "Jupiter");
+          done();
+
+          waitForAjax(function () {
+            $cancel = jQueryFrame($cancel.selector);
+            $popup = jQueryFrame($popup.selector);
+
+            $cancel.click();
+            return !$popup.hasClass("show");
+          }, function () {
+            $popup = jQueryFrame($popup.selector);
+            assert.notOk($popup.hasClass("show"));
+            done();
+
+            $saturn.click();
+
+            waitForAjax(function () {
+              $popup = jQueryFrame($popup.selector);
+              $name = jQueryFrame($name.selector);
+              return $popup.hasClass("show") && $name.val() === "Saturn";
+            }, function () {
+              $popup = jQueryFrame($popup.selector);
+              $name = jQueryFrame($name.selector);
+              assert.ok($popup.hasClass("show"));
+              assert.equal($name.val(), "Saturn");
+              done();
+
+
+              waitForAjax(function () {
+                $cancel = jQueryFrame($cancel.selector);
+                $popup = jQueryFrame($popup.selector);
+
+                $cancel.click();
+                return !$popup.hasClass("show");
+              }, function () {
+                $popup = jQueryFrame($popup.selector);
+                assert.notOk($popup.hasClass("show"));
+                done();
+              });
+            });
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.xhtml
index 5530d00..cd358ae 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/080-sheet/30-event/sheet-event.xhtml
@@ -24,7 +24,8 @@
                 xmlns:f="http://java.sun.com/jsf/core">
   <ui:param name="title" value="#{demoBundle.sheet_event} &lt;tc:row>"/>
   <p>A <code class="language-markup">&lt;tc:sheet/&gt;</code> can contain a
-    <code class="language-markup">&lt;tc:row/&gt;</code> tag with a containing <code class="language-markup">&lt;tc:event/&gt;</code> tag.
+    <code class="language-markup">&lt;tc:row/&gt;</code> tag with a containing <code
+            class="language-markup">&lt;tc:event/&gt;</code> tag.
     The event type can be set by the <code>event</code> attribute. Possible values are 'click' and 'dblclick'.
   </p>
 
@@ -62,8 +63,9 @@
           </tc:row>
 
           <tc:row id="sample3" rendered="#{sheetController.columnEventSample == 3}">
-            <f:ajax execute=":::popup" render=":::popup" listener="#{sheetController.selectSolarObject}" id="columnEventPopup"/>
-            <tc:event omit="true">
+            <tc:event>
+              <f:ajax id="columnEventPopup" execute=":::popup" render=":::popup"
+                      listener="#{sheetController.selectSolarObject}"/>
               <tc:operation name="show" for=":::popup"/>
             </tc:event>
           </tc:row>
@@ -94,7 +96,7 @@
 
       <tc:popup id="popup">
         <tc:box label="Details">
-          <tc:in label="Name" value="#{sheetController.selectedSolarObject.name}" required="true"/>
+          <tc:in id="popupName" label="Name" value="#{sheetController.selectedSolarObject.name}" required="true"/>
           <tc:in label="Number" value="#{sheetController.selectedSolarObject.number}"/>
           <tc:in label="Orbit" value="#{sheetController.selectedSolarObject.orbit}"/>
           <tc:in label="Distance" value="#{sheetController.selectedSolarObject.distance}"/>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/behavior.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/behavior.xhtml
index 0ee5657..d233191 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/behavior.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/30-concept/30-behavior/behavior.xhtml
@@ -28,7 +28,6 @@
     <li><code class="language-markup">&lt;f:ajax/></code> send an ajax request</li>
     <li><code class="language-markup">&lt;tc:event/></code> do a full page reload</li>
   </ul>
-  <p>You find more <tc:link label="examples" outcome="/content/40-test/6000-event/event.xhtml"/> in the test section.</p>
   <p>Tag Library Documentation:
     <tc:link label="&lt;f:ajax/>" image="#{request.contextPath}/image/feather-leaf.png"
              link="http://docs.oracle.com/javaee/6/javaserverfaces/2.1/docs/vdldocs/facelets/f/ajax.html"/>
@@ -36,7 +35,7 @@
     <tc:link label="&lt;tc:event/>" image="#{request.contextPath}/image/feather-leaf.png"
              link="#{demoBundle.tagDocUrl}/#{info.stableVersion}/tld/tc/event.html"/></p>
 
-  <tc:section label="Examples">
+  <tc:section label="Basic examples">
     <p>Type a text into the input field. After leaving the input field, the given text is shown in the output field.</p>
     <tc:section label="f:ajax">
       <pre><code class="language-markup">&lt;tc:in label="Ajax Input" value="\#{behaviorController.ajax}">
@@ -60,11 +59,42 @@
     </tc:section>
   </tc:section>
 
-  <tc:section label="Limitations">
-    <p>The <code class="language-markup">&lt;f:ajax></code> tag cannot inserted along with the
-      <code class="language-markup">&lt;tc:event></code> tag if both have the same <code>event</code> attribute value.
-      <br/>
-      The latter also applies if <code class="language-markup">&lt;f:ajax></code> is disabled
-      or if <code class="language-markup">&lt;tc:event></code> is not rendered.</p>
+  <tc:section label="Change the event name">
+    <p>It's possible to change the event name. For example if you want a double click event, change the event name to
+      <code>dblclick</code>. This is done for the following two buttons.</p>
+
+    <pre><code class="language-markup">&lt;tc:button label="Ajax Double Click">
+  &lt;f:ajax event="dblclick" render="outCounter" listener="..."/>
+&lt;/tc:button>
+&lt;tc:button label="Event Double Click">
+  &lt;tc:event event="dblclick" actionListener="..."/>
+&lt;/tc:button></code></pre>
+    <tc:button label="Ajax Double Click">
+      <f:ajax event="dblclick" render="outCounter" listener="#{behaviorController.countUp}"/>
+    </tc:button>
+    <tc:button label="Event Double Click">
+      <tc:event event="dblclick" actionListener="#{behaviorController.countUp}"/>
+    </tc:button>
+    <tc:out id="outCounter" label="Counter" value="#{behaviorController.counter}"/>
+  </tc:section>
+
+  <tc:section label="Advanced">
+    <p><code class="language-markup">&lt;tc:event></code> may contain <code class="language-markup">&lt;f:ajax></code>
+      and <code class="language-markup">&lt;tc:operation></code> tags.</p>
+    <p>This is helpfull if the parent component cannot handle operations by itself.</p>
+    <p><tc:badge value="Important" markup="danger"/>
+      The <code>event</code> attribute of the ajax children will be ignored.</p>
+
+    <tc:section label="Example">
+      <p>An example can be found for
+        <tc:link label="&lt;tc:row>" outcome="/content/20-component/080-sheet/30-event/sheet-event.xhtml"/>
+        Option: 'open popup on click with AJAX'.</p>
+      <pre><code class="language-markup">&lt;tc:row>
+  &lt;tc:event>
+    &lt;f:ajax execute=":::popup" render=":::popup" listener="..."/>
+    &lt;tc:operation name="show" for=":::popup"/>
+  &lt;/tc:event>
+&lt;/tc:row></code></pre>
+    </tc:section>
   </tc:section>
 </ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/6500-behavior/behavior.test.js b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/6500-behavior/behavior.test.js
new file mode 100644
index 0000000..56ce70e
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/6500-behavior/behavior.test.js
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+QUnit.test("Simple Event", function (assert) {
+  assert.expect(11);
+  var done = assert.async();
+  var step = 1;
+
+  var $button = jQueryFrame("#page\\:mainForm\\:simpleEvent");
+  var oldCounterValues = getCounterValues();
+
+  $button.click();
+
+  jQuery("#page\\:testframe").load(function () {
+    if (step === 1) {
+      compareCounterValues(assert, oldCounterValues, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0);
+
+      step++;
+      done();
+    }
+  });
+});
+
+QUnit.test("Simple Ajax", function (assert) {
+  assert.expect(11);
+  var done = assert.async();
+  var step = 1;
+
+  var $button = jQueryFrame("#page\\:mainForm\\:simpleAjax");
+  var oldCounterValues = getCounterValues();
+
+  $button.click();
+
+  waitForAjax(function () {
+    var newCounterValues = getCounterValues();
+    return step === 1 && oldCounterValues !== newCounterValues;
+  }, function () {
+    compareCounterValues(assert, oldCounterValues, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
+
+    step++;
+    done();
+  });
+});
+
+QUnit.test("Simple EventAjax", function (assert) {
+  assert.expect(11);
+  var done = assert.async();
+  var step = 1;
+
+  var $button = jQueryFrame("#page\\:mainForm\\:simpleEventAjax");
+  var oldCounterValues = getCounterValues();
+
+  $button.click();
+
+  waitForAjax(function () {
+    var newCounterValues = getCounterValues();
+    return step === 1 && oldCounterValues !== newCounterValues;
+  }, function () {
+    compareCounterValues(assert, oldCounterValues, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
+
+    step++;
+    done();
+  });
+});
+
+QUnit.test("Advanced Button: Option 1", function (assert) {
+  var $option = jQueryFrame("#page\\:mainForm\\:advancedSelector\\:\\:0");
+  var $button = jQueryFrame("#page\\:mainForm\\:advancedButton");
+  testEventOption(assert, $option, $button, "dblclick", 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0);
+});
+
+QUnit.test("Advanced Button: Option 2", function (assert) {
+  var $option = jQueryFrame("#page\\:mainForm\\:advancedSelector\\:\\:1");
+  var $button = jQueryFrame("#page\\:mainForm\\:advancedButton");
+
+  testAjaxOption(assert, $option, $button, "dblclick", 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1);
+});
+
+QUnit.test("Advanced Button: Option 3", function (assert) {
+  var $option = jQueryFrame("#page\\:mainForm\\:advancedSelector\\:\\:2");
+  var $button = jQueryFrame("#page\\:mainForm\\:advancedButton");
+  testAjaxOption(assert, $option, $button, "click", 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1);
+});
+
+QUnit.test("Row: Option 1", function (assert) {
+  var $option = jQueryFrame("#page\\:mainForm\\:advancedSelector\\:\\:0");
+  var $row = jQueryFrame("#page\\:mainForm\\:sheet\\:0\\:row");
+  testEventOption(assert, $option, $row, "dblclick", 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0);
+});
+
+QUnit.test("Row: Option 2", function (assert) {
+  var $option = jQueryFrame("#page\\:mainForm\\:advancedSelector\\:\\:1");
+  var $row = jQueryFrame("#page\\:mainForm\\:sheet\\:0\\:row");
+  testAjaxOption(assert, $option, $row, "dblclick", 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1);
+});
+
+QUnit.test("Row: Option 3", function (assert) {
+  var $option = jQueryFrame("#page\\:mainForm\\:advancedSelector\\:\\:2");
+  var $row = jQueryFrame("#page\\:mainForm\\:sheet\\:0\\:row");
+  testAjaxOption(assert, $option, $row, "click", 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1);
+});
+
+QUnit.test("Input: Click Event", function (assert) {
+  var $input = jQueryFrame("#page\\:mainForm\\:inputClick\\:\\:field");
+  testInputSection(assert, $input, "click");
+});
+
+function testEventOption(assert, $option, $component, event,
+                         buttonActionPlus, buttonActionListenerPlus,
+                         action1Plus, actionListener1Plus, ajaxListener1Plus,
+                         action2Plus, actionListener2Plus, ajaxListener2Plus,
+                         action3Plus, actionListener3Plus, ajaxListener3Plus) {
+  assert.expect(13);
+  var done = assert.async(3);
+  var step = 1;
+
+  var $hide = jQueryFrame("#page\\:mainForm\\:hideOperationTextBox");
+  var $operationOut = jQueryFrame("#page\\:mainForm\\:operationOut");
+  var oldCounterValues = getCounterValues();
+
+  $hide.click();
+
+  jQuery("#page\\:testframe").load(function () {
+    if (step === 1) {
+      $operationOut = jQueryFrame($operationOut.selector);
+      $option = jQueryFrame($option.selector);
+      assert.equal($operationOut.length, 0, "Content of operation test box must be hidden.");
+
+      $option.click();
+
+      step++;
+      done();
+    } else if (step === 2) {
+      $component = jQueryFrame($component.selector);
+      $component.trigger(event);
+
+      step++;
+      done();
+    } else if (step === 3) {
+      $operationOut = jQueryFrame($operationOut.selector);
+      assert.equal($operationOut.length, 1, "Content of operation test box must be shown.");
+
+      compareCounterValues(assert, oldCounterValues,
+          buttonActionPlus, buttonActionListenerPlus,
+          action1Plus, actionListener1Plus, ajaxListener1Plus,
+          action2Plus, actionListener2Plus, ajaxListener2Plus,
+          action3Plus, actionListener3Plus, ajaxListener3Plus);
+
+      step++;
+      done();
+    }
+  });
+}
+
+function testAjaxOption(assert, $option, $component, event,
+                        buttonActionPlus, buttonActionListenerPlus,
+                        action1Plus, actionListener1Plus, ajaxListener1Plus,
+                        action2Plus, actionListener2Plus, ajaxListener2Plus,
+                        action3Plus, actionListener3Plus, ajaxListener3Plus) {
+  assert.expect(13);
+  var done = assert.async(3);
+  var step = 1;
+
+  var $hide = jQueryFrame("#page\\:mainForm\\:hideOperationTextBox");
+  var $operationOut = jQueryFrame("#page\\:mainForm\\:operationOut");
+  var oldCounterValues = getCounterValues();
+
+  $hide.click();
+
+  jQuery("#page\\:testframe").load(function () {
+    if (step === 1) {
+      $operationOut = jQueryFrame($operationOut.selector);
+      $option = jQueryFrame($option.selector);
+      assert.equal($operationOut.length, 0, "Content of operation test box must be hidden.");
+
+      $option.click();
+
+      step++;
+      done();
+    } else if (step === 2) {
+      $component = jQueryFrame($component.selector);
+      $component.trigger(event);
+
+      step++;
+      done();
+
+      waitForAjax(function () {
+        var newCounterValues = getCounterValues();
+        return step === 3 && oldCounterValues !== newCounterValues;
+      }, function () {
+        $operationOut = jQueryFrame($operationOut.selector);
+        assert.equal($operationOut.length, 1, "Content of operation test box must be shown.");
+
+        compareCounterValues(assert, oldCounterValues,
+            buttonActionPlus, buttonActionListenerPlus,
+            action1Plus, actionListener1Plus, ajaxListener1Plus,
+            action2Plus, actionListener2Plus, ajaxListener2Plus,
+            action3Plus, actionListener3Plus, ajaxListener3Plus);
+
+        step++;
+        done();
+      });
+    }
+  });
+}
+
+function testInputSection(assert, $input, eventName) {
+  assert.expect(2);
+  var done = assert.async(2);
+  var step = 1;
+
+  var $hide = jQueryFrame("#page\\:mainForm\\:hideOperationTextBox");
+  $hide.click();
+
+  jQuery("#page\\:testframe").load(function () {
+    if (step === 1) {
+      $input = jQueryFrame($input.selector);
+      assert.ok(isOperationTestBoxCollapsed(), "Content of operation test box must be hidden.");
+
+      $input.trigger(eventName);
+
+      step++;
+      done();
+    } else if (step === 2) {
+      assert.notOk(isOperationTestBoxCollapsed(), "Content of operation test box must be shown.");
+
+      step++;
+      done();
+    }
+  });
+}
+
+function getCounterValues() {
+  var buttonActionCounter = jQueryFrame("#page\\:mainForm\\:buttonActionCounter.tobago-out").text();
+  var buttonActionListenerCounter = jQueryFrame("#page\\:mainForm\\:buttonActionListenerCounter.tobago-out").text();
+
+  var actionCount1 = jQueryFrame("#page\\:mainForm\\:actionCounter1.tobago-out").text();
+  var actionListenerCount1 = jQueryFrame("#page\\:mainForm\\:actionListenerCounter1.tobago-out").text();
+  var ajaxListenerCount1 = jQueryFrame("#page\\:mainForm\\:ajaxListenerCounter1.tobago-out").text();
+
+  var actionCount2 = jQueryFrame("#page\\:mainForm\\:actionCounter2.tobago-out").text();
+  var actionListenerCount2 = jQueryFrame("#page\\:mainForm\\:actionListenerCounter2.tobago-out").text();
+  var ajaxListenerCount2 = jQueryFrame("#page\\:mainForm\\:ajaxListenerCounter2.tobago-out").text();
+
+  var actionCount3 = jQueryFrame("#page\\:mainForm\\:actionCounter3.tobago-out").text();
+  var actionListenerCount3 = jQueryFrame("#page\\:mainForm\\:actionListenerCounter3.tobago-out").text();
+  var ajaxListenerCount3 = jQueryFrame("#page\\:mainForm\\:ajaxListenerCounter3.tobago-out").text();
+
+  return [buttonActionCounter, buttonActionListenerCounter,
+    actionCount1, actionListenerCount1, ajaxListenerCount1,
+    actionCount2, actionListenerCount2, ajaxListenerCount2,
+    actionCount3, actionListenerCount3, ajaxListenerCount3]
+}
+
+function compareCounterValues(assert, oldCounterValues,
+                              buttonActionPlus, buttonActionListenerPlus,
+                              action1Plus, actionListener1Plus, ajaxListener1Plus,
+                              action2Plus, actionListener2Plus, ajaxListener2Plus,
+                              action3Plus, actionListener3Plus, ajaxListener3Plus) {
+  var newCounterValues = getCounterValues();
+
+  assert.equal(parseInt(newCounterValues[0]), parseInt(oldCounterValues[0]) + buttonActionPlus);
+  assert.equal(parseInt(newCounterValues[1]), parseInt(oldCounterValues[1]) + buttonActionListenerPlus);
+  assert.equal(parseInt(newCounterValues[2]), parseInt(oldCounterValues[2]) + action1Plus);
+  assert.equal(parseInt(newCounterValues[3]), parseInt(oldCounterValues[3]) + actionListener1Plus);
+  assert.equal(parseInt(newCounterValues[4]), parseInt(oldCounterValues[4]) + ajaxListener1Plus);
+  assert.equal(parseInt(newCounterValues[5]), parseInt(oldCounterValues[5]) + action2Plus);
+  assert.equal(parseInt(newCounterValues[6]), parseInt(oldCounterValues[6]) + actionListener2Plus);
+  assert.equal(parseInt(newCounterValues[7]), parseInt(oldCounterValues[7]) + ajaxListener2Plus);
+  assert.equal(parseInt(newCounterValues[8]), parseInt(oldCounterValues[8]) + action3Plus);
+  assert.equal(parseInt(newCounterValues[9]), parseInt(oldCounterValues[9]) + actionListener3Plus);
+  assert.equal(parseInt(newCounterValues[10]), parseInt(oldCounterValues[10]) + ajaxListener3Plus);
+}
+
+function isOperationTestBoxCollapsed() {
+  var $operationOut = jQueryFrame("#page\\:mainForm\\:operationOut");
+  return $operationOut.length === 0;
+}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/6500-behavior/behavior.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/6500-behavior/behavior.xhtml
new file mode 100644
index 0000000..3a976c5
--- /dev/null
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/40-test/6500-behavior/behavior.xhtml
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ * 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.
+-->
+
+<ui:composition template="/main.xhtml"
+                xmlns="http://www.w3.org/1999/xhtml"
+                xmlns:f="http://java.sun.com/jsf/core"
+                xmlns:tc="http://myfaces.apache.org/tobago/component"
+                xmlns:ui="http://java.sun.com/jsf/facelets">
+  <ui:param name="title" value="#{demoBundle.behavior}"/>
+
+
+  <tc:box id="counter" label="Counter">
+    <f:facet name="bar">
+      <tc:button id="reset" label="Reset" action="#{behaviorTestController.reset}"/>
+    </f:facet>
+    <tc:segmentLayout extraSmall="3seg 2seg 4seg 3seg">
+      <tc:panel/>
+      <tc:out value="Action"/>
+      <tc:out value="ActionListener"/>
+      <tc:out value="AjaxListener"/>
+
+      <tc:out value="Button Counter" labelLayout="skip"/>
+      <tc:out id="buttonActionCounter" value="#{behaviorTestController.buttonActionCounter}" labelLayout="skip"/>
+      <tc:out id="buttonActionListenerCounter" value="#{behaviorTestController.buttonActionListenerCounter}"
+              labelLayout="skip"/>
+      <tc:panel/>
+
+      <tc:out value="Counter 1" labelLayout="skip"/>
+      <tc:out id="actionCounter1" value="#{behaviorTestController.actionCounter1}" labelLayout="skip"/>
+      <tc:out id="actionListenerCounter1" value="#{behaviorTestController.actionListenerCounter1}" labelLayout="skip"/>
+      <tc:out id="ajaxListenerCounter1" value="#{behaviorTestController.ajaxListenerCounter1}" labelLayout="skip"/>
+
+      <tc:out value="Counter 2" labelLayout="skip"/>
+      <tc:out id="actionCounter2" value="#{behaviorTestController.actionCounter2}" labelLayout="skip"/>
+      <tc:out id="actionListenerCounter2" value="#{behaviorTestController.actionListenerCounter2}" labelLayout="skip"/>
+      <tc:out id="ajaxListenerCounter2" value="#{behaviorTestController.ajaxListenerCounter2}" labelLayout="skip"/>
+
+      <tc:out value="Counter 3" labelLayout="skip"/>
+      <tc:out id="actionCounter3" value="#{behaviorTestController.actionCounter3}" labelLayout="skip"/>
+      <tc:out id="actionListenerCounter3" value="#{behaviorTestController.actionListenerCounter3}" labelLayout="skip"/>
+      <tc:out id="ajaxListenerCounter3" value="#{behaviorTestController.ajaxListenerCounter3}" labelLayout="skip"/>
+    </tc:segmentLayout>
+  </tc:box>
+  <tc:box id="operationTestBox" label="&lt;tc:operation> Test" collapsedMode="absent">
+    <f:facet name="bar">
+      <tc:button id="hideOperationTextBox" label="Hide">
+        <tc:operation name="hide" for="operationTestBox"/>
+      </tc:button>
+    </f:facet>
+    <tc:out id="operationOut" value="Content"/>
+  </tc:box>
+
+  <tc:section label="&lt;tc:button>">
+    <tc:section label="Basics">
+      <tc:button id="simpleEvent" label="Simple Event">
+        <tc:event action="#{behaviorTestController.countAction1}"
+                  actionListener="#{behaviorTestController.countActionListener1}"/>
+      </tc:button>
+      <tc:button id="simpleAjax" label="Simple Ajax">
+        <f:ajax render="counter operationTestBox" listener="#{behaviorTestController.countAjaxListener1}"/>
+      </tc:button>
+
+      <tc:button id="simpleEventAjax" label="Simple EventAjax">
+        <tc:event>
+          <f:ajax render="counter operationTestBox" listener="#{behaviorTestController.countAjaxListener1}"/>
+        </tc:event>
+      </tc:button>
+      <p/>
+    </tc:section>
+
+    <tc:section label="Advanced">
+      <tc:selectOneRadio id="advancedSelector" value="#{behaviorTestController.selector}">
+        <tc:selectItem itemValue="1" itemLabel="Event 1 + no Ajax enabled"/>
+        <tc:selectItem itemValue="2" itemLabel="Event 2 + Ajax 3 enabled"/>
+        <tc:selectItem itemValue="3" itemLabel="Event 3 + all Ajax enabled"/>
+        <tc:event action="#{behaviorTestController.submitSelection}"/>
+      </tc:selectOneRadio>
+      <tc:separator/>
+
+      <tc:button id="advancedButton" label="Advanced Button"
+                 action="#{behaviorTestController.countButtonAction}"
+                 actionListener="#{behaviorTestController.countButtonActionListener}">
+        <tc:event event="dblclick" action="#{behaviorTestController.countAction1}"
+                  actionListener="#{behaviorTestController.countActionListener1}"
+                  rendered="#{behaviorTestController.eventEnabled1}">
+          <f:ajax execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener1}"
+                  disabled="#{!behaviorTestController.ajaxEnabled1}"/>
+          <f:ajax event="blur" execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener2}"
+                  disabled="#{!behaviorTestController.ajaxEnabled2}"/>
+          <f:ajax event="focus" execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener3}"
+                  disabled="#{!behaviorTestController.ajaxEnabled3}"/>
+          <tc:operation name="show" for="operationTestBox"/>
+        </tc:event>
+        <tc:event event="dblclick" action="#{behaviorTestController.countAction2}"
+                  actionListener="#{behaviorTestController.countActionListener2}"
+                  disabled="#{!behaviorTestController.eventEnabled2}">
+          <f:ajax execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener1}"
+                  disabled="#{!behaviorTestController.ajaxEnabled1}"/>
+          <f:ajax event="blur" execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener2}"
+                  disabled="#{!behaviorTestController.ajaxEnabled2}"/>
+          <f:ajax event="focus" execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener3}"
+                  disabled="#{!behaviorTestController.ajaxEnabled3}"/>
+          <tc:operation name="show" for="operationTestBox"/>
+        </tc:event>
+        <tc:event action="#{behaviorTestController.countAction3}"
+                  actionListener="#{behaviorTestController.countActionListener3}"
+                  rendered="#{behaviorTestController.eventEnabled3}">
+          <f:ajax execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener1}"
+                  disabled="#{!behaviorTestController.ajaxEnabled1}"/>
+          <f:ajax event="blur" execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener2}"
+                  disabled="#{!behaviorTestController.ajaxEnabled2}"/>
+          <f:ajax event="focus" execute="operationTestBox" render="counter operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener3}"
+                  disabled="#{!behaviorTestController.ajaxEnabled3}"/>
+          <tc:operation name="show" for="operationTestBox"/>
+        </tc:event>
+      </tc:button>
+    </tc:section>
+  </tc:section>
+
+  <tc:section label="&lt;tc:row>">
+    <tc:sheet id="sheet" value="#{sheetController.solarList}" var="object" markup="small">
+      <tc:style maxHeight="100px"/>
+      <tc:column label="Solarobjects">
+        <tc:out value="#{object.name}" labelLayout="skip"/>
+      </tc:column>
+      <tc:row id="row">
+        <tc:event id="e1" event="dblclick" action="#{behaviorTestController.countAction1}"
+                  actionListener="#{behaviorTestController.countActionListener1}"
+                  rendered="#{behaviorTestController.eventEnabled1}">
+          <f:ajax execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener1}"
+                  disabled="#{!behaviorTestController.ajaxEnabled1}"/>
+          <f:ajax event="blur" execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener2}"
+                  disabled="#{!behaviorTestController.ajaxEnabled2}"/>
+          <f:ajax event="focus" execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener3}"
+                  disabled="#{!behaviorTestController.ajaxEnabled3}"/>
+          <tc:operation name="show" for=":::operationTestBox"/>
+        </tc:event>
+        <tc:event id="e2" event="dblclick" action="#{behaviorTestController.countAction2}"
+                  actionListener="#{behaviorTestController.countActionListener2}"
+                  disabled="#{!behaviorTestController.eventEnabled2}">
+          <f:ajax execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener1}"
+                  disabled="#{!behaviorTestController.ajaxEnabled1}"/>
+          <f:ajax event="blur" execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener2}"
+                  disabled="#{!behaviorTestController.ajaxEnabled2}"/>
+          <f:ajax event="focus" execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener3}"
+                  disabled="#{!behaviorTestController.ajaxEnabled3}"/>
+          <tc:operation name="show" for=":::operationTestBox"/>
+        </tc:event>
+        <tc:event id="e3" action="#{behaviorTestController.countAction3}"
+                  actionListener="#{behaviorTestController.countActionListener3}"
+                  rendered="#{behaviorTestController.eventEnabled3}">
+          <f:ajax execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener1}"
+                  disabled="#{!behaviorTestController.ajaxEnabled1}"/>
+          <f:ajax event="blur" execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener2}"
+                  disabled="#{!behaviorTestController.ajaxEnabled2}"/>
+          <f:ajax event="focus" execute=":::operationTestBox" render=":::counter :::operationTestBox"
+                  listener="#{behaviorTestController.countAjaxListener3}"
+                  disabled="#{!behaviorTestController.ajaxEnabled3}"/>
+          <tc:operation name="show" for=":::operationTestBox"/>
+        </tc:event>
+      </tc:row>
+    </tc:sheet>
+  </tc:section>
+
+  <tc:section label="&lt;tc:in>">
+    <tc:in id="inputClick" label="Event Input (click)">
+      <tc:event id="inputEventClick" event="click">
+        <tc:operation for="operationTestBox" name="show"/>
+      </tc:event>
+    </tc:in>
+  </tc:section>
+</ui:composition>
diff --git a/tobago-example/tobago-example-demo/src/test/java/org/apache/myfaces/tobago/example/demo/QUnitTests.java b/tobago-example/tobago-example-demo/src/test/java/org/apache/myfaces/tobago/example/demo/QUnitTests.java
index f49ef59..8f44840 100644
--- a/tobago-example/tobago-example-demo/src/test/java/org/apache/myfaces/tobago/example/demo/QUnitTests.java
+++ b/tobago-example/tobago-example-demo/src/test/java/org/apache/myfaces/tobago/example/demo/QUnitTests.java
@@ -555,4 +555,10 @@ public class QUnitTests {
     final String page = "content/40-test/50000-java/30-ajax-special-character/ajax-special-character.xhtml";
     runStandardTest(page);
   }
+
+  @Test
+  public void testBehavior() throws UnsupportedEncodingException, InterruptedException {
+    final String page = "content/40-test/6500-behavior/behavior.xhtml";
+    runStandardTest(page);
+  }
 }

-- 
To stop receiving notification emails like this one, please contact
hnoeth@apache.org.