You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2017/03/19 16:08:29 UTC

tapestry-5 git commit: TAP5-2225

Repository: tapestry-5
Updated Branches:
  refs/heads/master aaa0d550a -> 6b4ca30b8


TAP5-2225

Create client-side API to call a component's event handler methods

Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/6b4ca30b
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6b4ca30b
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6b4ca30b

Branch: refs/heads/master
Commit: 6b4ca30b83bed396006107f5008a3781eee1db86
Parents: aaa0d55
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
Authored: Sun Mar 19 13:07:00 2017 -0300
Committer: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
Committed: Sun Mar 19 13:07:00 2017 -0300

----------------------------------------------------------------------
 .../org/apache/tapestry5/TapestryConstants.java |  11 ++
 .../tapestry5/annotations/PublishEvent.java     |  50 ++++++
 .../corelib/mixins/PublishServerSideEvents.java | 151 +++++++++++++++++++
 .../tapestry5/internal/InternalConstants.java   |  17 +++
 .../internal/transform/OnEventWorker.java       |  54 ++++++-
 .../org/apache/tapestry5/t5-core-dom.coffee     |  56 +++++++
 .../src/test/app1/PublishEventDemo.tml          |   7 +
 .../components/PublishEventDemoComponent.java   |  32 ++++
 .../components/PublishEventDemoComponent2.java  |  32 ++++
 .../tapestry5/integration/app1/pages/Index.java |  21 +--
 .../app1/pages/PublishEventDemo.java            |  49 ++++++
 .../META-INF/assets/PublishEventDemo.js         |  11 ++
 .../components/PublishEventDemoComponent.tml    |   3 +
 .../components/PublishEventDemoComponent2.tml   |   4 +
 14 files changed, 482 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
index ad1495a..cab8c46 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
@@ -12,6 +12,7 @@
 
 package org.apache.tapestry5;
 
+import org.apache.tapestry5.annotations.PublishEvent;
 import org.apache.tapestry5.internal.structure.PageResetListener;
 import org.apache.tapestry5.services.ComponentEventLinkEncoder;
 
@@ -68,4 +69,14 @@ public class TapestryConstants
      */
     public static final String DISABLE_JAVASCRIPT_MINIMIZATION = "tapestry.disable-javascript-minimization";
 
+    /**
+     * Name of the HTML data attribute which contains information about component events
+     * published by using the {@linkplain PublishEvent} annotation
+     * in a component event handler method.
+     * 
+     * @see PublishEvent
+     * @since 5.4.2
+     */
+    public static final String COMPONENT_EVENTS_ATTRIBUTE_NAME = "data-component-events";
+
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
new file mode 100644
index 0000000..18239d4
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
@@ -0,0 +1,50 @@
+// Licensed 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.tapestry5.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.COMPONENT;
+import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.apache.tapestry5.ioc.annotations.UseWith;
+
+/**
+ * Marks an event handler method to be published as an event to be called in JavaScript
+ * through the <code>t5/core/triggerServerEvent</code> function.
+ * 
+ * The event information is stored in JSON format inside the 
+ * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME} attribute.
+ * 
+ * When used in a component method, the component must render at least one element,
+ * and that's what get the 
+ * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME} attribute above. 
+ * If it doesn't, an exception will be thrown.
+ * 
+ * When used in a page method, the page must render an &lt;body&gt; element,
+ * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}. If it doesn't,
+ * an exception will be thrown.
+ * 
+ * @since 5.4.2
+ */
+@Target(ElementType.METHOD)
+@Retention(RUNTIME)
+@Documented
+@UseWith({ COMPONENT, PAGE })
+public @interface PublishEvent
+{
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
new file mode 100644
index 0000000..3d73746
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
@@ -0,0 +1,151 @@
+// Licensed 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.tapestry5.corelib.mixins;
+
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.MarkupWriterListener;
+import org.apache.tapestry5.TapestryConstants;
+import org.apache.tapestry5.annotations.PublishEvent;
+import org.apache.tapestry5.dom.Element;
+import org.apache.tapestry5.internal.InternalConstants;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.json.JSONArray;
+import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.model.ComponentModel;
+
+/**
+ * Tapestry internal mixin used to implement the {@link PublishEvent} event logic. Don't use directly.
+ */
+public class PublishServerSideEvents
+{
+    
+    private static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY = InternalConstants.PUBLISH_COMPONENT_EVENTS_URL_PROPERTY;
+    private static final String COMPONENT_EVENTS_ATTRIBUTE_NAME = TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME;
+    
+    @Inject
+    private ComponentResources resources;
+    
+    void beginRender(final MarkupWriter writer) {
+    
+        final Element element = writer.getElement();
+        
+        // When the component is actually a page, nothing was rendered yet.
+        // The listener we add here will add the events attribute to the <body> element
+        // later
+        if (element == null) {
+            writer.addListener(new BodyElementListener(writer));
+        }
+        else {
+            writer.addListener(new DelayedListener(writer));
+        }
+        
+    }
+    
+    private void addEventsAttribute(final Element element)
+    {
+        
+        if (element == null)
+        {
+            throw new IllegalStateException("@PublishEvent used inside a page which didn't generate a <body> element");
+        }
+
+        final ComponentResources containerResources = resources.getContainerResources();
+        final ComponentModel componentModel = containerResources.getComponentModel();
+        final String metaValue = componentModel.getMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META);
+        final JSONArray componentEvents = new JSONArray(metaValue);
+        final JSONObject events = new JSONObject();
+        final String existingValue = element.getAttribute(COMPONENT_EVENTS_ATTRIBUTE_NAME);
+        
+        if (existingValue != null) 
+        {
+            final JSONObject existing = new JSONObject(existingValue);
+            for (String key : existing.keys()) {
+                events.put(key, existing.get(key));
+            }
+        }
+        
+        for (int i = 0; i < componentEvents.length(); i++) 
+        {
+            final String eventName = componentEvents.getString(i);
+            JSONObject event = new JSONObject();
+            event.put(PUBLISH_COMPONENT_EVENTS_URL_PROPERTY, containerResources.createEventLink(eventName).toString());
+            events.put(eventName, event);
+        }
+        
+        element.forceAttributes(TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME, events.toString());
+    }
+
+    final private class DelayedListener implements MarkupWriterListener {
+        
+        private MarkupWriter writer;
+        
+        private Element element;
+        
+        public DelayedListener(MarkupWriter writer)
+        {
+            super();
+            this.writer = writer;
+        }
+
+        @Override
+        public void elementDidStart(Element element)
+        {
+            // Store first element generated by rendering the component
+            if (this.element == null)
+            {
+                this.element = element;
+            }
+        }
+
+        @Override
+        public void elementDidEnd(Element element)
+        {
+            if (this.element == null)
+            {
+                throw new IllegalStateException("@PublishEvent used inside a component which didn't generate any HTML elements");
+            }
+            addEventsAttribute(this.element);
+            writer.removeListener(this);
+        }
+        
+    }
+    
+    final private class BodyElementListener implements MarkupWriterListener {
+        
+        private MarkupWriter writer;
+        
+        public BodyElementListener(MarkupWriter writer)
+        {
+            super();
+            this.writer = writer;
+        }
+
+        @Override
+        public void elementDidStart(Element element)
+        {
+            if (element.getName().equals("body")) 
+            {
+                addEventsAttribute(element);
+                writer.removeListener(this);
+            }
+        }
+
+        @Override
+        public void elementDidEnd(Element element)
+        {
+        }
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
index 32b80a6..8370ae0 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
@@ -13,8 +13,10 @@
 package org.apache.tapestry5.internal;
 
 import org.apache.tapestry5.ContentType;
+import org.apache.tapestry5.annotations.PublishEvent;
 import org.apache.tapestry5.dom.MarkupModel;
 import org.apache.tapestry5.ioc.util.TimeInterval;
+import org.apache.tapestry5.model.ComponentModel;
 import org.apache.tapestry5.services.javascript.JavaScriptStack;
 
 public final class InternalConstants
@@ -213,4 +215,19 @@ public final class InternalConstants
      * @since 5.4
      */
     public static final ContentType JAVASCRIPT_CONTENT_TYPE = new ContentType("text/javascript");
+    
+    /**
+     * Name of the {@linkplain ComponentModel} metadata key whiche stores the {@linkplain PublishEvent}
+     * data.
+     * @since 5.4.2
+     */
+    public static final String PUBLISH_COMPONENT_EVENTS_META = "meta.publish-component-events";
+    
+    /**
+     * Name of the JSONObject key name which holds the name of the event to be published.
+     * 
+     * @since 5.4.2
+     */
+    public static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY = "url";
+
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
index fb4502b..fdb2dc9 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
@@ -12,15 +12,23 @@
 
 package org.apache.tapestry5.internal.transform;
 
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
 import org.apache.tapestry5.ComponentResources;
 import org.apache.tapestry5.EventContext;
 import org.apache.tapestry5.ValueEncoder;
 import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.PublishEvent;
 import org.apache.tapestry5.annotations.RequestParameter;
+import org.apache.tapestry5.corelib.mixins.PublishServerSideEvents;
 import org.apache.tapestry5.func.F;
 import org.apache.tapestry5.func.Flow;
 import org.apache.tapestry5.func.Mapper;
 import org.apache.tapestry5.func.Predicate;
+import org.apache.tapestry5.internal.InternalConstants;
 import org.apache.tapestry5.internal.services.ComponentClassCache;
 import org.apache.tapestry5.ioc.OperationTracker;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
@@ -28,8 +36,19 @@ import org.apache.tapestry5.ioc.internal.util.InternalUtils;
 import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.ioc.util.ExceptionUtils;
 import org.apache.tapestry5.ioc.util.UnknownValueException;
+import org.apache.tapestry5.json.JSONArray;
 import org.apache.tapestry5.model.MutableComponentModel;
-import org.apache.tapestry5.plastic.*;
+import org.apache.tapestry5.plastic.Condition;
+import org.apache.tapestry5.plastic.InstructionBuilder;
+import org.apache.tapestry5.plastic.InstructionBuilderCallback;
+import org.apache.tapestry5.plastic.LocalVariable;
+import org.apache.tapestry5.plastic.LocalVariableCallback;
+import org.apache.tapestry5.plastic.MethodAdvice;
+import org.apache.tapestry5.plastic.MethodDescription;
+import org.apache.tapestry5.plastic.MethodInvocation;
+import org.apache.tapestry5.plastic.PlasticClass;
+import org.apache.tapestry5.plastic.PlasticField;
+import org.apache.tapestry5.plastic.PlasticMethod;
 import org.apache.tapestry5.runtime.ComponentEvent;
 import org.apache.tapestry5.runtime.Event;
 import org.apache.tapestry5.runtime.PageLifecycleListener;
@@ -39,11 +58,6 @@ import org.apache.tapestry5.services.ValueEncoderSource;
 import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
 import org.apache.tapestry5.services.transform.TransformationSupport;
 
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
 /**
  * Provides implementations of the
  * {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
@@ -151,6 +165,8 @@ public class OnEventWorker implements ComponentClassTransformWorker2
         int minContextValues = 0;
 
         boolean handleActivationEventContext = false;
+        
+        final PublishEvent publishEvent;
 
         EventHandlerMethod(PlasticMethod method)
         {
@@ -165,6 +181,8 @@ public class OnEventWorker implements ComponentClassTransformWorker2
 
             eventType = extractEventType(methodName, onEvent);
             componentId = extractComponentId(methodName, onEvent);
+            
+            publishEvent = method.getAnnotation(PublishEvent.class);
         }
 
         void buildMatchAndInvocation(InstructionBuilder builder, final LocalVariable resultVariable)
@@ -343,9 +361,31 @@ public class OnEventWorker implements ComponentClassTransformWorker2
         implementDispatchMethod(plasticClass, isRoot, model, eventHandlerMethods);
 
         addComponentIdValidationLogicOnPageLoad(plasticClass, eventHandlerMethods);
+        
+        addPublishEventInfo(eventHandlerMethods, model);
+    }
+
+    private void addPublishEventInfo(Flow<EventHandlerMethod> eventHandlerMethods,
+            MutableComponentModel model)
+    {
+        JSONArray publishEvents = new JSONArray();
+        for (EventHandlerMethod eventHandlerMethod : eventHandlerMethods)
+        {
+            if (eventHandlerMethod.publishEvent != null)
+            {
+                publishEvents.put(eventHandlerMethod.eventType.toLowerCase());
+            }
+        }
+        
+        // If we do have events to publish, we apply the mixin and pass
+        // event information to it.
+        if (publishEvents.length() > 0) {
+            model.addMixinClassName(PublishServerSideEvents.class.getName(), "after:*");
+            model.setMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META, publishEvents.toString());
+        }
     }
 
-    private void addComponentIdValidationLogicOnPageLoad(PlasticClass plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
+	private void addComponentIdValidationLogicOnPageLoad(PlasticClass plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
     {
         ComponentIdValidator[] validators = extractComponentIdValidators(eventHandlerMethods);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
index 9d419bc..9b975d4 100644
--- a/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
+++ b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
@@ -906,6 +906,60 @@ define ["underscore", "./utils", "./events", "jquery"],
       events = utils.split events
 #endif
       onevent elements, events, match, handler
+      
+    # Returns the URL of a component event based on its name and an optional element
+    # or null if the event information is not found. When the element isn't passed
+    # or it's null, the event data is taken from the <body> element.
+    #
+    # * eventName - (string) name of the component event
+    # * element - (object) HTML DOM element to be used as the begining of the event data search. Optional.
+    getEventUrl = (eventName, element) ->
+
+      if not (eventName?)
+        throw 'dom.getEventUrl: the eventName parameter cannot be null'
+
+      if not _.isString eventName
+        throw 'dom.getEventUrl: the eventName parameter should be a string'
+        
+      eventName = eventName.toLowerCase()
+        
+      if element is null
+        element = document.getElementsByTagName('body')[0]
+
+      # Look for event data in itself first, then in the preceding siblings
+      # if not found
+      url = null
+  
+      while not url? and element.previousElementSibling?
+        data = getDataAttributeAsObject(element, 'component-events')
+        url = data?[eventName]?.url
+        element = element.previousElementSibling
+
+      if not url?
+    
+        # Look at parent elements recursively
+        while not url? and element.parentElement?
+          data = getDataAttributeAsObject(element, 'component-events')
+          url = data?[eventName]?.url
+          element = element.parentElement;
+      
+      return url;      
+  	    
+    # Returns the value of a given data attribute as an object.
+    # The "data-" prefix is added automatically.
+    # element - (object) HTML dom element
+    # attribute - (string) name of the data attribute without the "data-" prefix.
+    getDataAttributeAsObject = (element, attribute) ->
+    	
+#if jquery
+      value = $(element).data(attribute)
+#elseif prototype
+      value = JSON.parse($(element).readAttribute('data-' + attribute))
+      if value isnt null
+        value = JSON.parse(value)
+      else 
+        value = {} 
+#endif
 
     # onDocument() is used to add an event handler to the document object; this is used
     # for global (or default) handlers.
@@ -919,5 +973,7 @@ define ["underscore", "./utils", "./events", "jquery"],
     body: wrapElement document.body
 
     scanner: scanner
+    
+    getEventUrl : getEventUrl
 
   return exports

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/app1/PublishEventDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/PublishEventDemo.tml b/tapestry-core/src/test/app1/PublishEventDemo.tml
new file mode 100644
index 0000000..43c2bd7
--- /dev/null
+++ b/tapestry-core/src/test/app1/PublishEventDemo.tml
@@ -0,0 +1,7 @@
+<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"
+          xmlns:p="tapestry:parameter">
+    <div id="page">
+	    <t:PublishEventDemoComponent/>
+	    <t:PublishEventDemoComponent2/>
+    </div>
+</t:border>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java
new file mode 100644
index 0000000..b8054f9
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java
@@ -0,0 +1,32 @@
+// Licensed 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.tapestry5.integration.app1.components;
+
+import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.PublishEvent;
+import org.apache.tapestry5.json.JSONObject;
+
+public class PublishEventDemoComponent
+{
+    @OnEvent("answer")
+    @PublishEvent
+    JSONObject answer() {
+        return new JSONObject("origin", "component");
+    }
+    
+    @PublishEvent
+    JSONObject onAction()
+    {
+        return new JSONObject("origin", "component");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java
new file mode 100644
index 0000000..7771557
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java
@@ -0,0 +1,32 @@
+// Licensed 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.tapestry5.integration.app1.components;
+
+import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.PublishEvent;
+import org.apache.tapestry5.json.JSONObject;
+
+public class PublishEventDemoComponent2
+{
+    @PublishEvent
+    JSONObject onAction()
+    {
+        return new JSONObject("origin", "component");
+    }
+    
+    @OnEvent("answer")
+    @PublishEvent
+    JSONObject answer() {
+        return new JSONObject("origin", "component");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
index d7d76d6..e78a874 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
@@ -57,32 +57,35 @@ public class Index
 
     private static final List<Item> ITEMS = CollectionFactory
             .newList(
-                    new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."),
+                    
+                  new Item("PublishEventDemo", "@PublishEvent Demo", "Publishing server-side events to client-side code (JavaScript)"),
+
+//                    new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."),
 
-                    new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."),
+//                    new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."),
 
                     new Item("OverrideEventHandlerDemo", "Event Handler Override Demo", "Event Handler methods overridden by sub-classes invoke base-class correctly."),
 
-                    new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."),
+//                    new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."),
 
                     new Item("MissingRequiredARP", "Missing Query Parameter for @ActivationRequestParameter", "Activating a page with a required @ActivationRequestParameter, but no matching query parameter, is an error."),
 
-                    new Item("DateFieldValidationDemo", "DateField Validation Demo",
-                            "Use of DateField component when client validation is disabled."),
+//                    new Item("DateFieldValidationDemo", "DateField Validation Demo",
+//                            "Use of DateField component when client validation is disabled."),
 
                     new Item("MixinParameters54", "Strict Mixin Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the mixin id."),
 
-                    new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."),
+//                    new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."),
 
                     new Item("FormCancelActionDemo", "Form Cancel Action Demo", "FormSupport.addCancel() support"),
 
                     new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio components inside an Ajax form"),
 
-                    new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"),
+//                    new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"),
 
-                    new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"),
+//                    new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"),
 
-                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."),
+//                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."),
 
                     new Item("MissingAssetDemo", "Missing Asset Demo", "Error when injecting an asset that does not exist."),
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
new file mode 100644
index 0000000..1876de5
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
@@ -0,0 +1,49 @@
+// Copyright 2016 The Apache Software Foundation
+//
+// Licensed 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.
+
+// Licensed 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.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.Import;
+import org.apache.tapestry5.annotations.OnEvent;
+import org.apache.tapestry5.annotations.PublishEvent;
+import org.apache.tapestry5.json.JSONObject;
+
+@Import(stack = "core", library = "PublishEventDemo.js")
+public class PublishEventDemo
+{
+
+    @PublishEvent
+    JSONObject onAction()
+    {
+        return new JSONObject("origin", "page");
+    }
+    
+    @OnEvent("answer")
+    @PublishEvent
+    JSONObject answer() {
+        return new JSONObject("origin", "page");
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
new file mode 100644
index 0000000..20c0406
--- /dev/null
+++ b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
@@ -0,0 +1,11 @@
+require(["t5/core/dom", "t5/core/ajax", "jquery"], function (dom, ajax, $) {
+
+    $(document).ready(function() {
+        console.log('dom.getEventURL()   : ' + dom.getEventUrl('answer', document.getElementById("page")));
+        console.log('dom.getEventURL() 1 : ' + dom.getEventUrl('answer', document.getElementById("componentParagraph")));
+        console.log('dom.getEventURL() 2 : ' + dom.getEventUrl('answer', document.getElementById("componentParagraph2")));
+        console.log('dom.getEventURL() 3 : ' + dom.getEventUrl('answer', document.getElementById("componentParagraph3")));
+    });
+    
+});
+

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml
new file mode 100644
index 0000000..2d2b5f1
--- /dev/null
+++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml
@@ -0,0 +1,3 @@
+<div id="component" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+	<p id="componentParagraph">I'm a component</p>
+</div>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
new file mode 100644
index 0000000..b10d020
--- /dev/null
+++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
@@ -0,0 +1,4 @@
+<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
+	<p id="componentParagraph2">I'm another component</p>
+	<p id="componentParagraph3">I'm another component</p>
+</t:container>


Re: tapestry-5 git commit: TAP5-2225

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
Thanks, Massimo! It's something I wanted to do for a long time, but hadn't
find the time to sit down and implement it. I'm documenting it now.

By the way, it's been added both to 5.4 and 5.5, as nothing in it needs
Java 8.

On Sun, Mar 19, 2017 at 3:09 PM, Massimo Lusetti <ml...@gmail.com> wrote:

> Cool and long awaited feature.
>
> On Sun, Mar 19, 2017 at 5:08 PM,  <th...@apache.org> wrote:
> > Repository: tapestry-5
> > Updated Branches:
> >   refs/heads/master aaa0d550a -> 6b4ca30b8
> >
> >
> > TAP5-2225
> >
> > Create client-side API to call a component's event handler methods
> >
> > Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
> > Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/
> 6b4ca30b
> > Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6b4ca30b
> > Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6b4ca30b
> >
> > Branch: refs/heads/master
> > Commit: 6b4ca30b83bed396006107f5008a3781eee1db86
> > Parents: aaa0d55
> > Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
> > Authored: Sun Mar 19 13:07:00 2017 -0300
> > Committer: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
> > Committed: Sun Mar 19 13:07:00 2017 -0300
> >
> > ----------------------------------------------------------------------
> >  .../org/apache/tapestry5/TapestryConstants.java |  11 ++
> >  .../tapestry5/annotations/PublishEvent.java     |  50 ++++++
> >  .../corelib/mixins/PublishServerSideEvents.java | 151
> +++++++++++++++++++
> >  .../tapestry5/internal/InternalConstants.java   |  17 +++
> >  .../internal/transform/OnEventWorker.java       |  54 ++++++-
> >  .../org/apache/tapestry5/t5-core-dom.coffee     |  56 +++++++
> >  .../src/test/app1/PublishEventDemo.tml          |   7 +
> >  .../components/PublishEventDemoComponent.java   |  32 ++++
> >  .../components/PublishEventDemoComponent2.java  |  32 ++++
> >  .../tapestry5/integration/app1/pages/Index.java |  21 +--
> >  .../app1/pages/PublishEventDemo.java            |  49 ++++++
> >  .../META-INF/assets/PublishEventDemo.js         |  11 ++
> >  .../components/PublishEventDemoComponent.tml    |   3 +
> >  .../components/PublishEventDemoComponent2.tml   |   4 +
> >  14 files changed, 482 insertions(+), 16 deletions(-)
> > ----------------------------------------------------------------------
> >
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/
> tapestry5/TapestryConstants.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> > index ad1495a..cab8c46 100644
> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/
> TapestryConstants.java
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/
> TapestryConstants.java
> > @@ -12,6 +12,7 @@
> >
> >  package org.apache.tapestry5;
> >
> > +import org.apache.tapestry5.annotations.PublishEvent;
> >  import org.apache.tapestry5.internal.structure.PageResetListener;
> >  import org.apache.tapestry5.services.ComponentEventLinkEncoder;
> >
> > @@ -68,4 +69,14 @@ public class TapestryConstants
> >       */
> >      public static final String DISABLE_JAVASCRIPT_MINIMIZATION =
> "tapestry.disable-javascript-minimization";
> >
> > +    /**
> > +     * Name of the HTML data attribute which contains information about
> component events
> > +     * published by using the {@linkplain PublishEvent} annotation
> > +     * in a component event handler method.
> > +     *
> > +     * @see PublishEvent
> > +     * @since 5.4.2
> > +     */
> > +    public static final String COMPONENT_EVENTS_ATTRIBUTE_NAME =
> "data-component-events";
> > +
> >  }
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/
> PublishEvent.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/
> annotations/PublishEvent.java
> > new file mode 100644
> > index 0000000..18239d4
> > --- /dev/null
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/
> annotations/PublishEvent.java
> > @@ -0,0 +1,50 @@
> > +// Licensed 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.tapestry5.annotations;
> > +
> > +import static java.lang.annotation.RetentionPolicy.RUNTIME;
> > +import static org.apache.tapestry5.ioc.annotations.
> AnnotationUseContext.COMPONENT;
> > +import static org.apache.tapestry5.ioc.annotations.
> AnnotationUseContext.PAGE;
> > +
> > +import java.lang.annotation.Documented;
> > +import java.lang.annotation.ElementType;
> > +import java.lang.annotation.Retention;
> > +import java.lang.annotation.Target;
> > +
> > +import org.apache.tapestry5.ioc.annotations.UseWith;
> > +
> > +/**
> > + * Marks an event handler method to be published as an event to be
> called in JavaScript
> > + * through the <code>t5/core/triggerServerEvent</code> function.
> > + *
> > + * The event information is stored in JSON format inside the
> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}
> attribute.
> > + *
> > + * When used in a component method, the component must render at least
> one element,
> > + * and that's what get the
> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}
> attribute above.
> > + * If it doesn't, an exception will be thrown.
> > + *
> > + * When used in a page method, the page must render an &lt;body&gt;
> element,
> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}.
> If it doesn't,
> > + * an exception will be thrown.
> > + *
> > + * @since 5.4.2
> > + */
> > +@Target(ElementType.METHOD)
> > +@Retention(RUNTIME)
> > +@Documented
> > +@UseWith({ COMPONENT, PAGE })
> > +public @interface PublishEvent
> > +{
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/
> PublishServerSideEvents.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/
> mixins/PublishServerSideEvents.java b/tapestry-core/src/main/java/
> org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
> > new file mode 100644
> > index 0000000..3d73746
> > --- /dev/null
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/
> PublishServerSideEvents.java
> > @@ -0,0 +1,151 @@
> > +// Licensed 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.tapestry5.corelib.mixins;
> > +
> > +import org.apache.tapestry5.ComponentResources;
> > +import org.apache.tapestry5.MarkupWriter;
> > +import org.apache.tapestry5.MarkupWriterListener;
> > +import org.apache.tapestry5.TapestryConstants;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.dom.Element;
> > +import org.apache.tapestry5.internal.InternalConstants;
> > +import org.apache.tapestry5.ioc.annotations.Inject;
> > +import org.apache.tapestry5.json.JSONArray;
> > +import org.apache.tapestry5.json.JSONObject;
> > +import org.apache.tapestry5.model.ComponentModel;
> > +
> > +/**
> > + * Tapestry internal mixin used to implement the {@link PublishEvent}
> event logic. Don't use directly.
> > + */
> > +public class PublishServerSideEvents
> > +{
> > +
> > +    private static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY
> = InternalConstants.PUBLISH_COMPONENT_EVENTS_URL_PROPERTY;
> > +    private static final String COMPONENT_EVENTS_ATTRIBUTE_NAME =
> TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME;
> > +
> > +    @Inject
> > +    private ComponentResources resources;
> > +
> > +    void beginRender(final MarkupWriter writer) {
> > +
> > +        final Element element = writer.getElement();
> > +
> > +        // When the component is actually a page, nothing was rendered
> yet.
> > +        // The listener we add here will add the events attribute to
> the <body> element
> > +        // later
> > +        if (element == null) {
> > +            writer.addListener(new BodyElementListener(writer));
> > +        }
> > +        else {
> > +            writer.addListener(new DelayedListener(writer));
> > +        }
> > +
> > +    }
> > +
> > +    private void addEventsAttribute(final Element element)
> > +    {
> > +
> > +        if (element == null)
> > +        {
> > +            throw new IllegalStateException("@PublishEvent used inside
> a page which didn't generate a <body> element");
> > +        }
> > +
> > +        final ComponentResources containerResources = resources.
> getContainerResources();
> > +        final ComponentModel componentModel = containerResources.
> getComponentModel();
> > +        final String metaValue = componentModel.getMeta(
> InternalConstants.PUBLISH_COMPONENT_EVENTS_META);
> > +        final JSONArray componentEvents = new JSONArray(metaValue);
> > +        final JSONObject events = new JSONObject();
> > +        final String existingValue = element.getAttribute(
> COMPONENT_EVENTS_ATTRIBUTE_NAME);
> > +
> > +        if (existingValue != null)
> > +        {
> > +            final JSONObject existing = new JSONObject(existingValue);
> > +            for (String key : existing.keys()) {
> > +                events.put(key, existing.get(key));
> > +            }
> > +        }
> > +
> > +        for (int i = 0; i < componentEvents.length(); i++)
> > +        {
> > +            final String eventName = componentEvents.getString(i);
> > +            JSONObject event = new JSONObject();
> > +            event.put(PUBLISH_COMPONENT_EVENTS_URL_PROPERTY,
> containerResources.createEventLink(eventName).toString());
> > +            events.put(eventName, event);
> > +        }
> > +
> > +        element.forceAttributes(TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME,
> events.toString());
> > +    }
> > +
> > +    final private class DelayedListener implements MarkupWriterListener
> {
> > +
> > +        private MarkupWriter writer;
> > +
> > +        private Element element;
> > +
> > +        public DelayedListener(MarkupWriter writer)
> > +        {
> > +            super();
> > +            this.writer = writer;
> > +        }
> > +
> > +        @Override
> > +        public void elementDidStart(Element element)
> > +        {
> > +            // Store first element generated by rendering the component
> > +            if (this.element == null)
> > +            {
> > +                this.element = element;
> > +            }
> > +        }
> > +
> > +        @Override
> > +        public void elementDidEnd(Element element)
> > +        {
> > +            if (this.element == null)
> > +            {
> > +                throw new IllegalStateException("@PublishEvent used
> inside a component which didn't generate any HTML elements");
> > +            }
> > +            addEventsAttribute(this.element);
> > +            writer.removeListener(this);
> > +        }
> > +
> > +    }
> > +
> > +    final private class BodyElementListener implements
> MarkupWriterListener {
> > +
> > +        private MarkupWriter writer;
> > +
> > +        public BodyElementListener(MarkupWriter writer)
> > +        {
> > +            super();
> > +            this.writer = writer;
> > +        }
> > +
> > +        @Override
> > +        public void elementDidStart(Element element)
> > +        {
> > +            if (element.getName().equals("body"))
> > +            {
> > +                addEventsAttribute(element);
> > +                writer.removeListener(this);
> > +            }
> > +        }
> > +
> > +        @Override
> > +        public void elementDidEnd(Element element)
> > +        {
> > +        }
> > +
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > index 32b80a6..8370ae0 100644
> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > @@ -13,8 +13,10 @@
> >  package org.apache.tapestry5.internal;
> >
> >  import org.apache.tapestry5.ContentType;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> >  import org.apache.tapestry5.dom.MarkupModel;
> >  import org.apache.tapestry5.ioc.util.TimeInterval;
> > +import org.apache.tapestry5.model.ComponentModel;
> >  import org.apache.tapestry5.services.javascript.JavaScriptStack;
> >
> >  public final class InternalConstants
> > @@ -213,4 +215,19 @@ public final class InternalConstants
> >       * @since 5.4
> >       */
> >      public static final ContentType JAVASCRIPT_CONTENT_TYPE = new
> ContentType("text/javascript");
> > +
> > +    /**
> > +     * Name of the {@linkplain ComponentModel} metadata key whiche
> stores the {@linkplain PublishEvent}
> > +     * data.
> > +     * @since 5.4.2
> > +     */
> > +    public static final String PUBLISH_COMPONENT_EVENTS_META =
> "meta.publish-component-events";
> > +
> > +    /**
> > +     * Name of the JSONObject key name which holds the name of the
> event to be published.
> > +     *
> > +     * @since 5.4.2
> > +     */
> > +    public static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY =
> "url";
> > +
> >  }
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/
> tapestry5/internal/transform/OnEventWorker.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> transform/OnEventWorker.java
> > index fb4502b..fdb2dc9 100644
> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> transform/OnEventWorker.java
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> transform/OnEventWorker.java
> > @@ -12,15 +12,23 @@
> >
> >  package org.apache.tapestry5.internal.transform;
> >
> > +import java.lang.reflect.Array;
> > +import java.util.Arrays;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> >  import org.apache.tapestry5.ComponentResources;
> >  import org.apache.tapestry5.EventContext;
> >  import org.apache.tapestry5.ValueEncoder;
> >  import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> >  import org.apache.tapestry5.annotations.RequestParameter;
> > +import org.apache.tapestry5.corelib.mixins.PublishServerSideEvents;
> >  import org.apache.tapestry5.func.F;
> >  import org.apache.tapestry5.func.Flow;
> >  import org.apache.tapestry5.func.Mapper;
> >  import org.apache.tapestry5.func.Predicate;
> > +import org.apache.tapestry5.internal.InternalConstants;
> >  import org.apache.tapestry5.internal.services.ComponentClassCache;
> >  import org.apache.tapestry5.ioc.OperationTracker;
> >  import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> > @@ -28,8 +36,19 @@ import org.apache.tapestry5.ioc.
> internal.util.InternalUtils;
> >  import org.apache.tapestry5.ioc.internal.util.TapestryException;
> >  import org.apache.tapestry5.ioc.util.ExceptionUtils;
> >  import org.apache.tapestry5.ioc.util.UnknownValueException;
> > +import org.apache.tapestry5.json.JSONArray;
> >  import org.apache.tapestry5.model.MutableComponentModel;
> > -import org.apache.tapestry5.plastic.*;
> > +import org.apache.tapestry5.plastic.Condition;
> > +import org.apache.tapestry5.plastic.InstructionBuilder;
> > +import org.apache.tapestry5.plastic.InstructionBuilderCallback;
> > +import org.apache.tapestry5.plastic.LocalVariable;
> > +import org.apache.tapestry5.plastic.LocalVariableCallback;
> > +import org.apache.tapestry5.plastic.MethodAdvice;
> > +import org.apache.tapestry5.plastic.MethodDescription;
> > +import org.apache.tapestry5.plastic.MethodInvocation;
> > +import org.apache.tapestry5.plastic.PlasticClass;
> > +import org.apache.tapestry5.plastic.PlasticField;
> > +import org.apache.tapestry5.plastic.PlasticMethod;
> >  import org.apache.tapestry5.runtime.ComponentEvent;
> >  import org.apache.tapestry5.runtime.Event;
> >  import org.apache.tapestry5.runtime.PageLifecycleListener;
> > @@ -39,11 +58,6 @@ import org.apache.tapestry5.services.
> ValueEncoderSource;
> >  import org.apache.tapestry5.services.transform.
> ComponentClassTransformWorker2;
> >  import org.apache.tapestry5.services.transform.TransformationSupport;
> >
> > -import java.lang.reflect.Array;
> > -import java.util.Arrays;
> > -import java.util.List;
> > -import java.util.Map;
> > -
> >  /**
> >   * Provides implementations of the
> >   * {@link org.apache.tapestry5.runtime.Component#
> dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
> > @@ -151,6 +165,8 @@ public class OnEventWorker implements
> ComponentClassTransformWorker2
> >          int minContextValues = 0;
> >
> >          boolean handleActivationEventContext = false;
> > +
> > +        final PublishEvent publishEvent;
> >
> >          EventHandlerMethod(PlasticMethod method)
> >          {
> > @@ -165,6 +181,8 @@ public class OnEventWorker implements
> ComponentClassTransformWorker2
> >
> >              eventType = extractEventType(methodName, onEvent);
> >              componentId = extractComponentId(methodName, onEvent);
> > +
> > +            publishEvent = method.getAnnotation(PublishEvent.class);
> >          }
> >
> >          void buildMatchAndInvocation(InstructionBuilder builder, final
> LocalVariable resultVariable)
> > @@ -343,9 +361,31 @@ public class OnEventWorker implements
> ComponentClassTransformWorker2
> >          implementDispatchMethod(plasticClass, isRoot, model,
> eventHandlerMethods);
> >
> >          addComponentIdValidationLogicOnPageLoad(plasticClass,
> eventHandlerMethods);
> > +
> > +        addPublishEventInfo(eventHandlerMethods, model);
> > +    }
> > +
> > +    private void addPublishEventInfo(Flow<EventHandlerMethod>
> eventHandlerMethods,
> > +            MutableComponentModel model)
> > +    {
> > +        JSONArray publishEvents = new JSONArray();
> > +        for (EventHandlerMethod eventHandlerMethod :
> eventHandlerMethods)
> > +        {
> > +            if (eventHandlerMethod.publishEvent != null)
> > +            {
> > +                publishEvents.put(eventHandlerMethod.eventType.
> toLowerCase());
> > +            }
> > +        }
> > +
> > +        // If we do have events to publish, we apply the mixin and pass
> > +        // event information to it.
> > +        if (publishEvents.length() > 0) {
> > +            model.addMixinClassName(PublishServerSideEvents.class.getName(),
> "after:*");
> > +            model.setMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META,
> publishEvents.toString());
> > +        }
> >      }
> >
> > -    private void addComponentIdValidationLogicOnPageLoad(PlasticClass
> plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
> > +       private void addComponentIdValidationLogicOnPageLoad(PlasticClass
> plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
> >      {
> >          ComponentIdValidator[] validators =
> extractComponentIdValidators(eventHandlerMethods);
> >
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/
> tapestry5/t5-core-dom.coffee
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee b/tapestry-core/src/main/
> preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
> > index 9d419bc..9b975d4 100644
> > --- a/tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee
> > +++ b/tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee
> > @@ -906,6 +906,60 @@ define ["underscore", "./utils", "./events",
> "jquery"],
> >        events = utils.split events
> >  #endif
> >        onevent elements, events, match, handler
> > +
> > +    # Returns the URL of a component event based on its name and an
> optional element
> > +    # or null if the event information is not found. When the element
> isn't passed
> > +    # or it's null, the event data is taken from the <body> element.
> > +    #
> > +    # * eventName - (string) name of the component event
> > +    # * element - (object) HTML DOM element to be used as the begining
> of the event data search. Optional.
> > +    getEventUrl = (eventName, element) ->
> > +
> > +      if not (eventName?)
> > +        throw 'dom.getEventUrl: the eventName parameter cannot be null'
> > +
> > +      if not _.isString eventName
> > +        throw 'dom.getEventUrl: the eventName parameter should be a
> string'
> > +
> > +      eventName = eventName.toLowerCase()
> > +
> > +      if element is null
> > +        element = document.getElementsByTagName('body')[0]
> > +
> > +      # Look for event data in itself first, then in the preceding
> siblings
> > +      # if not found
> > +      url = null
> > +
> > +      while not url? and element.previousElementSibling?
> > +        data = getDataAttributeAsObject(element, 'component-events')
> > +        url = data?[eventName]?.url
> > +        element = element.previousElementSibling
> > +
> > +      if not url?
> > +
> > +        # Look at parent elements recursively
> > +        while not url? and element.parentElement?
> > +          data = getDataAttributeAsObject(element, 'component-events')
> > +          url = data?[eventName]?.url
> > +          element = element.parentElement;
> > +
> > +      return url;
> > +
> > +    # Returns the value of a given data attribute as an object.
> > +    # The "data-" prefix is added automatically.
> > +    # element - (object) HTML dom element
> > +    # attribute - (string) name of the data attribute without the
> "data-" prefix.
> > +    getDataAttributeAsObject = (element, attribute) ->
> > +
> > +#if jquery
> > +      value = $(element).data(attribute)
> > +#elseif prototype
> > +      value = JSON.parse($(element).readAttribute('data-' + attribute))
> > +      if value isnt null
> > +        value = JSON.parse(value)
> > +      else
> > +        value = {}
> > +#endif
> >
> >      # onDocument() is used to add an event handler to the document
> object; this is used
> >      # for global (or default) handlers.
> > @@ -919,5 +973,7 @@ define ["underscore", "./utils", "./events",
> "jquery"],
> >      body: wrapElement document.body
> >
> >      scanner: scanner
> > +
> > +    getEventUrl : getEventUrl
> >
> >    return exports
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/app1/PublishEventDemo.tml
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/app1/PublishEventDemo.tml
> b/tapestry-core/src/test/app1/PublishEventDemo.tml
> > new file mode 100644
> > index 0000000..43c2bd7
> > --- /dev/null
> > +++ b/tapestry-core/src/test/app1/PublishEventDemo.tml
> > @@ -0,0 +1,7 @@
> > +<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd
> "
> > +          xmlns:p="tapestry:parameter">
> > +    <div id="page">
> > +           <t:PublishEventDemoComponent/>
> > +           <t:PublishEventDemoComponent2/>
> > +    </div>
> > +</t:border>
> > \ No newline at end of file
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent.java
> > new file mode 100644
> > index 0000000..b8054f9
> > --- /dev/null
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent.java
> > @@ -0,0 +1,32 @@
> > +// Licensed 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.tapestry5.integration.app1.components;
> > +
> > +import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.json.JSONObject;
> > +
> > +public class PublishEventDemoComponent
> > +{
> > +    @OnEvent("answer")
> > +    @PublishEvent
> > +    JSONObject answer() {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +    @PublishEvent
> > +    JSONObject onAction()
> > +    {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent2.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent2.java
> > new file mode 100644
> > index 0000000..7771557
> > --- /dev/null
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent2.java
> > @@ -0,0 +1,32 @@
> > +// Licensed 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.tapestry5.integration.app1.components;
> > +
> > +import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.json.JSONObject;
> > +
> > +public class PublishEventDemoComponent2
> > +{
> > +    @PublishEvent
> > +    JSONObject onAction()
> > +    {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +    @OnEvent("answer")
> > +    @PublishEvent
> > +    JSONObject answer() {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/pages/Index.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/Index.java b/tapestry-core/src/test/java/
> org/apache/tapestry5/integration/app1/pages/Index.java
> > index d7d76d6..e78a874 100644
> > --- a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/Index.java
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/Index.java
> > @@ -57,32 +57,35 @@ public class Index
> >
> >      private static final List<Item> ITEMS = CollectionFactory
> >              .newList(
> > -                    new Item("ZoneFormDemo", "Zone Form Decoration",
> "Fields inside an Ajax-updatd Form are still decorated properly."),
> > +
> > +                  new Item("PublishEventDemo", "@PublishEvent Demo",
> "Publishing server-side events to client-side code (JavaScript)"),
> > +
> > +//                    new Item("ZoneFormDemo", "Zone Form Decoration",
> "Fields inside an Ajax-updatd Form are still decorated properly."),
> >
> > -                    new Item("AjaxValidationDemo", "Ajax Validation",
> "Demonstrated proper integration of server-side validation and client-side
> field decoration."),
> > +//                    new Item("AjaxValidationDemo", "Ajax Validation",
> "Demonstrated proper integration of server-side validation and client-side
> field decoration."),
> >
> >                      new Item("OverrideEventHandlerDemo", "Event
> Handler Override Demo", "Event Handler methods overridden by sub-classes
> invoke base-class correctly."),
> >
> > -                    new Item("LogoSubclass", "Base class Assets in
> sub-classes", "Assets are resolved for the parent class if that's where the
> annotations are."),
> > +//                    new Item("LogoSubclass", "Base class Assets in
> sub-classes", "Assets are resolved for the parent class if that's where the
> annotations are."),
> >
> >                      new Item("MissingRequiredARP", "Missing Query
> Parameter for @ActivationRequestParameter", "Activating a page with a
> required @ActivationRequestParameter, but no matching query parameter, is
> an error."),
> >
> > -                    new Item("DateFieldValidationDemo", "DateField
> Validation Demo",
> > -                            "Use of DateField component when client
> validation is disabled."),
> > +//                    new Item("DateFieldValidationDemo", "DateField
> Validation Demo",
> > +//                            "Use of DateField component when client
> validation is disabled."),
> >
> >                      new Item("MixinParameters54", "Strict Mixin
> Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the
> mixin id."),
> >
> > -                    new Item("AsyncDemo", "Async Links and Forms Demo",
> "Async (XHR) Updates without a containing Zone."),
> > +//                    new Item("AsyncDemo", "Async Links and Forms
> Demo", "Async (XHR) Updates without a containing Zone."),
> >
> >                      new Item("FormCancelActionDemo", "Form Cancel
> Action Demo", "FormSupport.addCancel() support"),
> >
> >                      new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio
> components inside an Ajax form"),
> >
> > -                    new Item("TimeIntervalDemo", "TimeInterval Demo",
> "Interval component, based on Moment.js"),
> > +//                    new Item("TimeIntervalDemo", "TimeInterval Demo",
> "Interval component, based on Moment.js"),
> >
> > -                    new Item("LocalDateDemo", "LocalDate Demo",
> "LocalDate component, based on Moment.js"),
> > +//                    new Item("LocalDateDemo", "LocalDate Demo",
> "LocalDate component, based on Moment.js"),
> >
> > -                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure an
> empty If can still render."),
> > +//                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure
> an empty If can still render."),
> >
> >                      new Item("MissingAssetDemo", "Missing Asset Demo",
> "Error when injecting an asset that does not exist."),
> >
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/pages/PublishEventDemo.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/PublishEventDemo.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/
> PublishEventDemo.java
> > new file mode 100644
> > index 0000000..1876de5
> > --- /dev/null
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/PublishEventDemo.java
> > @@ -0,0 +1,49 @@
> > +// Copyright 2016 The Apache Software Foundation
> > +//
> > +// Licensed 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.
> > +
> > +// Licensed 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.tapestry5.integration.app1.pages;
> > +
> > +import org.apache.tapestry5.annotations.Import;
> > +import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.json.JSONObject;
> > +
> > +@Import(stack = "core", library = "PublishEventDemo.js")
> > +public class PublishEventDemo
> > +{
> > +
> > +    @PublishEvent
> > +    JSONObject onAction()
> > +    {
> > +        return new JSONObject("origin", "page");
> > +    }
> > +
> > +    @OnEvent("answer")
> > +    @PublishEvent
> > +    JSONObject answer() {
> > +        return new JSONObject("origin", "page");
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/resources/META-INF/
> assets/PublishEventDemo.js
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> > new file mode 100644
> > index 0000000..20c0406
> > --- /dev/null
> > +++ b/tapestry-core/src/test/resources/META-INF/assets/
> PublishEventDemo.js
> > @@ -0,0 +1,11 @@
> > +require(["t5/core/dom", "t5/core/ajax", "jquery"], function (dom, ajax,
> $) {
> > +
> > +    $(document).ready(function() {
> > +        console.log('dom.getEventURL()   : ' +
> dom.getEventUrl('answer', document.getElementById("page")));
> > +        console.log('dom.getEventURL() 1 : ' +
> dom.getEventUrl('answer', document.getElementById("componentParagraph")));
> > +        console.log('dom.getEventURL() 2 : ' +
> dom.getEventUrl('answer', document.getElementById("
> componentParagraph2")));
> > +        console.log('dom.getEventURL() 3 : ' +
> dom.getEventUrl('answer', document.getElementById("
> componentParagraph3")));
> > +    });
> > +
> > +});
> > +
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/
> components/PublishEventDemoComponent.tml
> > new file mode 100644
> > index 0000000..2d2b5f1
> > --- /dev/null
> > +++ b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> > @@ -0,0 +1,3 @@
> > +<div id="component" xmlns:t="http://tapestry.
> apache.org/schema/tapestry_5_0_0.xsd">
> > +       <p id="componentParagraph">I'm a component</p>
> > +</div>
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/
> components/PublishEventDemoComponent2.tml
> > new file mode 100644
> > index 0000000..b10d020
> > --- /dev/null
> > +++ b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> > @@ -0,0 +1,4 @@
> > +<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_
> 0_0.xsd">
> > +       <p id="componentParagraph2">I'm another component</p>
> > +       <p id="componentParagraph3">I'm another component</p>
> > +</t:container>
> >
>
>
>
> --
> Massimo Lusetti
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: dev-help@tapestry.apache.org
>
>


-- 
Thiago

Re: tapestry-5 git commit: TAP5-2225

Posted by "Thiago H. de Paula Figueiredo" <th...@gmail.com>.
On Sun, Mar 19, 2017 at 3:42 PM, Dimitris Zenios <di...@gmail.com>
wrote:

> My only concern is with
> /tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee
>
> +#if jquery
> +      value = $(element).data(attribute)
> +#elseif prototype
> +      value = JSON.parse($(element).readAttribute('data-' + attribute))
> +      if value isnt null
> +        value = JSON.parse(value)
> +      else
> +        value = {}
> +#endif
>
> What happens when not jquery neither prototype is used.Instead it should
> use the dom api.
>

Unless you're talking about running Tapestry without any traces of jQuery
nor Prototype, which isn't supported, then this won't be a problem. But you
raise an interesting point. :) I did research for a framework-free, vanilla
JS solution for reading data attributes, which actually exists (the DOM
dataset property, described in
https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes),
but it's not supported by Internet Explorer 10 and older versions. I'd love
to see Tapestry provide a vanilla JS infrastructure, but browsers,
specially IE, are not there yet.

-- 
Thiago

Re: tapestry-5 git commit: TAP5-2225

Posted by Dimitris Zenios <di...@gmail.com>.
Sorry.my mistake.though this file was not the dom file

On 19 Mar 2017 20:42, "Dimitris Zenios" <di...@gmail.com> wrote:

> My only concern is with /tapestry-core/src/main/
> preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
>
> +#if jquery
> +      value = $(element).data(attribute)
> +#elseif prototype
> +      value = JSON.parse($(element).readAttribute('data-' + attribute))
> +      if value isnt null
> +        value = JSON.parse(value)
> +      else
> +        value = {}
> +#endif
>
> What happens when not jquery neither prototype is used.Instead it should
> use the dom api.
>
> Shall i post this to the issue itself?
>
> On Sun, Mar 19, 2017 at 8:09 PM, Massimo Lusetti <ml...@gmail.com>
> wrote:
>
>> Cool and long awaited feature.
>>
>> On Sun, Mar 19, 2017 at 5:08 PM,  <th...@apache.org> wrote:
>> > Repository: tapestry-5
>> > Updated Branches:
>> >   refs/heads/master aaa0d550a -> 6b4ca30b8
>> >
>> >
>> > TAP5-2225
>> >
>> > Create client-side API to call a component's event handler methods
>> >
>> > Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
>> > Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/6b4
>> ca30b
>> > Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6b4ca30b
>> > Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6b4ca30b
>> >
>> > Branch: refs/heads/master
>> > Commit: 6b4ca30b83bed396006107f5008a3781eee1db86
>> > Parents: aaa0d55
>> > Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
>> > Authored: Sun Mar 19 13:07:00 2017 -0300
>> > Committer: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
>> > Committed: Sun Mar 19 13:07:00 2017 -0300
>> >
>> > ----------------------------------------------------------------------
>> >  .../org/apache/tapestry5/TapestryConstants.java |  11 ++
>> >  .../tapestry5/annotations/PublishEvent.java     |  50 ++++++
>> >  .../corelib/mixins/PublishServerSideEvents.java | 151
>> +++++++++++++++++++
>> >  .../tapestry5/internal/InternalConstants.java   |  17 +++
>> >  .../internal/transform/OnEventWorker.java       |  54 ++++++-
>> >  .../org/apache/tapestry5/t5-core-dom.coffee     |  56 +++++++
>> >  .../src/test/app1/PublishEventDemo.tml          |   7 +
>> >  .../components/PublishEventDemoComponent.java   |  32 ++++
>> >  .../components/PublishEventDemoComponent2.java  |  32 ++++
>> >  .../tapestry5/integration/app1/pages/Index.java |  21 +--
>> >  .../app1/pages/PublishEventDemo.java            |  49 ++++++
>> >  .../META-INF/assets/PublishEventDemo.js         |  11 ++
>> >  .../components/PublishEventDemoComponent.tml    |   3 +
>> >  .../components/PublishEventDemoComponent2.tml   |   4 +
>> >  14 files changed, 482 insertions(+), 16 deletions(-)
>> > ----------------------------------------------------------------------
>> >
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/main/java/org/apache/tapestry5/
>> TapestryConstants.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
>> b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
>> > index ad1495a..cab8c46 100644
>> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryC
>> onstants.java
>> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryC
>> onstants.java
>> > @@ -12,6 +12,7 @@
>> >
>> >  package org.apache.tapestry5;
>> >
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> >  import org.apache.tapestry5.internal.structure.PageResetListener;
>> >  import org.apache.tapestry5.services.ComponentEventLinkEncoder;
>> >
>> > @@ -68,4 +69,14 @@ public class TapestryConstants
>> >       */
>> >      public static final String DISABLE_JAVASCRIPT_MINIMIZATION =
>> "tapestry.disable-javascript-minimization";
>> >
>> > +    /**
>> > +     * Name of the HTML data attribute which contains information
>> about component events
>> > +     * published by using the {@linkplain PublishEvent} annotation
>> > +     * in a component event handler method.
>> > +     *
>> > +     * @see PublishEvent
>> > +     * @since 5.4.2
>> > +     */
>> > +    public static final String COMPONENT_EVENTS_ATTRIBUTE_NAME =
>> "data-component-events";
>> > +
>> >  }
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/main/java/org/apache/tapestry5/
>> annotations/PublishEvent.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
>> b/tapestry-core/src/main/java/org/apache/tapestry5/annotatio
>> ns/PublishEvent.java
>> > new file mode 100644
>> > index 0000000..18239d4
>> > --- /dev/null
>> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/annotatio
>> ns/PublishEvent.java
>> > @@ -0,0 +1,50 @@
>> > +// Licensed 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.tapestry5.annotations;
>> > +
>> > +import static java.lang.annotation.RetentionPolicy.RUNTIME;
>> > +import static org.apache.tapestry5.ioc.annot
>> ations.AnnotationUseContext.COMPONENT;
>> > +import static org.apache.tapestry5.ioc.annot
>> ations.AnnotationUseContext.PAGE;
>> > +
>> > +import java.lang.annotation.Documented;
>> > +import java.lang.annotation.ElementType;
>> > +import java.lang.annotation.Retention;
>> > +import java.lang.annotation.Target;
>> > +
>> > +import org.apache.tapestry5.ioc.annotations.UseWith;
>> > +
>> > +/**
>> > + * Marks an event handler method to be published as an event to be
>> called in JavaScript
>> > + * through the <code>t5/core/triggerServerEvent</code> function.
>> > + *
>> > + * The event information is stored in JSON format inside the
>> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}
>> attribute.
>> > + *
>> > + * When used in a component method, the component must render at least
>> one element,
>> > + * and that's what get the
>> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}
>> attribute above.
>> > + * If it doesn't, an exception will be thrown.
>> > + *
>> > + * When used in a page method, the page must render an &lt;body&gt;
>> element,
>> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}.
>> If it doesn't,
>> > + * an exception will be thrown.
>> > + *
>> > + * @since 5.4.2
>> > + */
>> > +@Target(ElementType.METHOD)
>> > +@Retention(RUNTIME)
>> > +@Documented
>> > +@UseWith({ COMPONENT, PAGE })
>> > +public @interface PublishEvent
>> > +{
>> > +}
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/main/java/org/apache/tapestry5/
>> corelib/mixins/PublishServerSideEvents.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/m
>> ixins/PublishServerSideEvents.java b/tapestry-core/src/main/java/
>> org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
>> > new file mode 100644
>> > index 0000000..3d73746
>> > --- /dev/null
>> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/m
>> ixins/PublishServerSideEvents.java
>> > @@ -0,0 +1,151 @@
>> > +// Licensed 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.tapestry5.corelib.mixins;
>> > +
>> > +import org.apache.tapestry5.ComponentResources;
>> > +import org.apache.tapestry5.MarkupWriter;
>> > +import org.apache.tapestry5.MarkupWriterListener;
>> > +import org.apache.tapestry5.TapestryConstants;
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> > +import org.apache.tapestry5.dom.Element;
>> > +import org.apache.tapestry5.internal.InternalConstants;
>> > +import org.apache.tapestry5.ioc.annotations.Inject;
>> > +import org.apache.tapestry5.json.JSONArray;
>> > +import org.apache.tapestry5.json.JSONObject;
>> > +import org.apache.tapestry5.model.ComponentModel;
>> > +
>> > +/**
>> > + * Tapestry internal mixin used to implement the {@link PublishEvent}
>> event logic. Don't use directly.
>> > + */
>> > +public class PublishServerSideEvents
>> > +{
>> > +
>> > +    private static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY
>> = InternalConstants.PUBLISH_COMPONENT_EVENTS_URL_PROPERTY;
>> > +    private static final String COMPONENT_EVENTS_ATTRIBUTE_NAME =
>> TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME;
>> > +
>> > +    @Inject
>> > +    private ComponentResources resources;
>> > +
>> > +    void beginRender(final MarkupWriter writer) {
>> > +
>> > +        final Element element = writer.getElement();
>> > +
>> > +        // When the component is actually a page, nothing was rendered
>> yet.
>> > +        // The listener we add here will add the events attribute to
>> the <body> element
>> > +        // later
>> > +        if (element == null) {
>> > +            writer.addListener(new BodyElementListener(writer));
>> > +        }
>> > +        else {
>> > +            writer.addListener(new DelayedListener(writer));
>> > +        }
>> > +
>> > +    }
>> > +
>> > +    private void addEventsAttribute(final Element element)
>> > +    {
>> > +
>> > +        if (element == null)
>> > +        {
>> > +            throw new IllegalStateException("@PublishEvent used
>> inside a page which didn't generate a <body> element");
>> > +        }
>> > +
>> > +        final ComponentResources containerResources =
>> resources.getContainerResources();
>> > +        final ComponentModel componentModel =
>> containerResources.getComponentModel();
>> > +        final String metaValue = componentModel.getMeta(Interna
>> lConstants.PUBLISH_COMPONENT_EVENTS_META);
>> > +        final JSONArray componentEvents = new JSONArray(metaValue);
>> > +        final JSONObject events = new JSONObject();
>> > +        final String existingValue = element.getAttribute(COMPONENT
>> _EVENTS_ATTRIBUTE_NAME);
>> > +
>> > +        if (existingValue != null)
>> > +        {
>> > +            final JSONObject existing = new JSONObject(existingValue);
>> > +            for (String key : existing.keys()) {
>> > +                events.put(key, existing.get(key));
>> > +            }
>> > +        }
>> > +
>> > +        for (int i = 0; i < componentEvents.length(); i++)
>> > +        {
>> > +            final String eventName = componentEvents.getString(i);
>> > +            JSONObject event = new JSONObject();
>> > +            event.put(PUBLISH_COMPONENT_EVENTS_URL_PROPERTY,
>> containerResources.createEventLink(eventName).toString());
>> > +            events.put(eventName, event);
>> > +        }
>> > +
>> > +        element.forceAttributes(TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME,
>> events.toString());
>> > +    }
>> > +
>> > +    final private class DelayedListener implements
>> MarkupWriterListener {
>> > +
>> > +        private MarkupWriter writer;
>> > +
>> > +        private Element element;
>> > +
>> > +        public DelayedListener(MarkupWriter writer)
>> > +        {
>> > +            super();
>> > +            this.writer = writer;
>> > +        }
>> > +
>> > +        @Override
>> > +        public void elementDidStart(Element element)
>> > +        {
>> > +            // Store first element generated by rendering the component
>> > +            if (this.element == null)
>> > +            {
>> > +                this.element = element;
>> > +            }
>> > +        }
>> > +
>> > +        @Override
>> > +        public void elementDidEnd(Element element)
>> > +        {
>> > +            if (this.element == null)
>> > +            {
>> > +                throw new IllegalStateException("@PublishEvent used
>> inside a component which didn't generate any HTML elements");
>> > +            }
>> > +            addEventsAttribute(this.element);
>> > +            writer.removeListener(this);
>> > +        }
>> > +
>> > +    }
>> > +
>> > +    final private class BodyElementListener implements
>> MarkupWriterListener {
>> > +
>> > +        private MarkupWriter writer;
>> > +
>> > +        public BodyElementListener(MarkupWriter writer)
>> > +        {
>> > +            super();
>> > +            this.writer = writer;
>> > +        }
>> > +
>> > +        @Override
>> > +        public void elementDidStart(Element element)
>> > +        {
>> > +            if (element.getName().equals("body"))
>> > +            {
>> > +                addEventsAttribute(element);
>> > +                writer.removeListener(this);
>> > +            }
>> > +        }
>> > +
>> > +        @Override
>> > +        public void elementDidEnd(Element element)
>> > +        {
>> > +        }
>> > +
>> > +    }
>> > +
>> > +}
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/main/java/org/apache/tapestry5/
>> internal/InternalConstants.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
>> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
>> InternalConstants.java
>> > index 32b80a6..8370ae0 100644
>> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/
>> InternalConstants.java
>> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
>> InternalConstants.java
>> > @@ -13,8 +13,10 @@
>> >  package org.apache.tapestry5.internal;
>> >
>> >  import org.apache.tapestry5.ContentType;
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> >  import org.apache.tapestry5.dom.MarkupModel;
>> >  import org.apache.tapestry5.ioc.util.TimeInterval;
>> > +import org.apache.tapestry5.model.ComponentModel;
>> >  import org.apache.tapestry5.services.javascript.JavaScriptStack;
>> >
>> >  public final class InternalConstants
>> > @@ -213,4 +215,19 @@ public final class InternalConstants
>> >       * @since 5.4
>> >       */
>> >      public static final ContentType JAVASCRIPT_CONTENT_TYPE = new
>> ContentType("text/javascript");
>> > +
>> > +    /**
>> > +     * Name of the {@linkplain ComponentModel} metadata key whiche
>> stores the {@linkplain PublishEvent}
>> > +     * data.
>> > +     * @since 5.4.2
>> > +     */
>> > +    public static final String PUBLISH_COMPONENT_EVENTS_META =
>> "meta.publish-component-events";
>> > +
>> > +    /**
>> > +     * Name of the JSONObject key name which holds the name of the
>> event to be published.
>> > +     *
>> > +     * @since 5.4.2
>> > +     */
>> > +    public static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY
>> = "url";
>> > +
>> >  }
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/main/java/org/apache/tapestry5/
>> internal/transform/OnEventWorker.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
>> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
>> transform/OnEventWorker.java
>> > index fb4502b..fdb2dc9 100644
>> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/
>> transform/OnEventWorker.java
>> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
>> transform/OnEventWorker.java
>> > @@ -12,15 +12,23 @@
>> >
>> >  package org.apache.tapestry5.internal.transform;
>> >
>> > +import java.lang.reflect.Array;
>> > +import java.util.Arrays;
>> > +import java.util.List;
>> > +import java.util.Map;
>> > +
>> >  import org.apache.tapestry5.ComponentResources;
>> >  import org.apache.tapestry5.EventContext;
>> >  import org.apache.tapestry5.ValueEncoder;
>> >  import org.apache.tapestry5.annotations.OnEvent;
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> >  import org.apache.tapestry5.annotations.RequestParameter;
>> > +import org.apache.tapestry5.corelib.mixins.PublishServerSideEvents;
>> >  import org.apache.tapestry5.func.F;
>> >  import org.apache.tapestry5.func.Flow;
>> >  import org.apache.tapestry5.func.Mapper;
>> >  import org.apache.tapestry5.func.Predicate;
>> > +import org.apache.tapestry5.internal.InternalConstants;
>> >  import org.apache.tapestry5.internal.services.ComponentClassCache;
>> >  import org.apache.tapestry5.ioc.OperationTracker;
>> >  import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
>> > @@ -28,8 +36,19 @@ import org.apache.tapestry5.ioc.inter
>> nal.util.InternalUtils;
>> >  import org.apache.tapestry5.ioc.internal.util.TapestryException;
>> >  import org.apache.tapestry5.ioc.util.ExceptionUtils;
>> >  import org.apache.tapestry5.ioc.util.UnknownValueException;
>> > +import org.apache.tapestry5.json.JSONArray;
>> >  import org.apache.tapestry5.model.MutableComponentModel;
>> > -import org.apache.tapestry5.plastic.*;
>> > +import org.apache.tapestry5.plastic.Condition;
>> > +import org.apache.tapestry5.plastic.InstructionBuilder;
>> > +import org.apache.tapestry5.plastic.InstructionBuilderCallback;
>> > +import org.apache.tapestry5.plastic.LocalVariable;
>> > +import org.apache.tapestry5.plastic.LocalVariableCallback;
>> > +import org.apache.tapestry5.plastic.MethodAdvice;
>> > +import org.apache.tapestry5.plastic.MethodDescription;
>> > +import org.apache.tapestry5.plastic.MethodInvocation;
>> > +import org.apache.tapestry5.plastic.PlasticClass;
>> > +import org.apache.tapestry5.plastic.PlasticField;
>> > +import org.apache.tapestry5.plastic.PlasticMethod;
>> >  import org.apache.tapestry5.runtime.ComponentEvent;
>> >  import org.apache.tapestry5.runtime.Event;
>> >  import org.apache.tapestry5.runtime.PageLifecycleListener;
>> > @@ -39,11 +58,6 @@ import org.apache.tapestry5.services.
>> ValueEncoderSource;
>> >  import org.apache.tapestry5.services.transform.ComponentClassTransf
>> ormWorker2;
>> >  import org.apache.tapestry5.services.transform.TransformationSupport;
>> >
>> > -import java.lang.reflect.Array;
>> > -import java.util.Arrays;
>> > -import java.util.List;
>> > -import java.util.Map;
>> > -
>> >  /**
>> >   * Provides implementations of the
>> >   * {@link org.apache.tapestry5.runtime.Component#dispatchComponentEven
>> t(org.apache.tapestry5.runtime.ComponentEvent)}
>> > @@ -151,6 +165,8 @@ public class OnEventWorker implements
>> ComponentClassTransformWorker2
>> >          int minContextValues = 0;
>> >
>> >          boolean handleActivationEventContext = false;
>> > +
>> > +        final PublishEvent publishEvent;
>> >
>> >          EventHandlerMethod(PlasticMethod method)
>> >          {
>> > @@ -165,6 +181,8 @@ public class OnEventWorker implements
>> ComponentClassTransformWorker2
>> >
>> >              eventType = extractEventType(methodName, onEvent);
>> >              componentId = extractComponentId(methodName, onEvent);
>> > +
>> > +            publishEvent = method.getAnnotation(PublishEvent.class);
>> >          }
>> >
>> >          void buildMatchAndInvocation(InstructionBuilder builder,
>> final LocalVariable resultVariable)
>> > @@ -343,9 +361,31 @@ public class OnEventWorker implements
>> ComponentClassTransformWorker2
>> >          implementDispatchMethod(plasticClass, isRoot, model,
>> eventHandlerMethods);
>> >
>> >          addComponentIdValidationLogicOnPageLoad(plasticClass,
>> eventHandlerMethods);
>> > +
>> > +        addPublishEventInfo(eventHandlerMethods, model);
>> > +    }
>> > +
>> > +    private void addPublishEventInfo(Flow<EventHandlerMethod>
>> eventHandlerMethods,
>> > +            MutableComponentModel model)
>> > +    {
>> > +        JSONArray publishEvents = new JSONArray();
>> > +        for (EventHandlerMethod eventHandlerMethod :
>> eventHandlerMethods)
>> > +        {
>> > +            if (eventHandlerMethod.publishEvent != null)
>> > +            {
>> > +                publishEvents.put(eventHandler
>> Method.eventType.toLowerCase());
>> > +            }
>> > +        }
>> > +
>> > +        // If we do have events to publish, we apply the mixin and pass
>> > +        // event information to it.
>> > +        if (publishEvents.length() > 0) {
>> > +            model.addMixinClassName(Publis
>> hServerSideEvents.class.getName(), "after:*");
>> > +            model.setMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META,
>> publishEvents.toString());
>> > +        }
>> >      }
>> >
>> > -    private void addComponentIdValidationLogicOnPageLoad(PlasticClass
>> plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
>> > +       private void addComponentIdValidationLogicOnPageLoad(PlasticClass
>> plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
>> >      {
>> >          ComponentIdValidator[] validators =
>> extractComponentIdValidators(eventHandlerMethods);
>> >
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/main/preprocessed-coffeescript/org/
>> apache/tapestry5/t5-core-dom.coffee
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
>> b/tapestry-core/src/main/preprocessed-coffeescript/org/apach
>> e/tapestry5/t5-core-dom.coffee
>> > index 9d419bc..9b975d4 100644
>> > --- a/tapestry-core/src/main/preprocessed-coffeescript/org/apach
>> e/tapestry5/t5-core-dom.coffee
>> > +++ b/tapestry-core/src/main/preprocessed-coffeescript/org/apach
>> e/tapestry5/t5-core-dom.coffee
>> > @@ -906,6 +906,60 @@ define ["underscore", "./utils", "./events",
>> "jquery"],
>> >        events = utils.split events
>> >  #endif
>> >        onevent elements, events, match, handler
>> > +
>> > +    # Returns the URL of a component event based on its name and an
>> optional element
>> > +    # or null if the event information is not found. When the element
>> isn't passed
>> > +    # or it's null, the event data is taken from the <body> element.
>> > +    #
>> > +    # * eventName - (string) name of the component event
>> > +    # * element - (object) HTML DOM element to be used as the begining
>> of the event data search. Optional.
>> > +    getEventUrl = (eventName, element) ->
>> > +
>> > +      if not (eventName?)
>> > +        throw 'dom.getEventUrl: the eventName parameter cannot be null'
>> > +
>> > +      if not _.isString eventName
>> > +        throw 'dom.getEventUrl: the eventName parameter should be a
>> string'
>> > +
>> > +      eventName = eventName.toLowerCase()
>> > +
>> > +      if element is null
>> > +        element = document.getElementsByTagName('body')[0]
>> > +
>> > +      # Look for event data in itself first, then in the preceding
>> siblings
>> > +      # if not found
>> > +      url = null
>> > +
>> > +      while not url? and element.previousElementSibling?
>> > +        data = getDataAttributeAsObject(element, 'component-events')
>> > +        url = data?[eventName]?.url
>> > +        element = element.previousElementSibling
>> > +
>> > +      if not url?
>> > +
>> > +        # Look at parent elements recursively
>> > +        while not url? and element.parentElement?
>> > +          data = getDataAttributeAsObject(element, 'component-events')
>> > +          url = data?[eventName]?.url
>> > +          element = element.parentElement;
>> > +
>> > +      return url;
>> > +
>> > +    # Returns the value of a given data attribute as an object.
>> > +    # The "data-" prefix is added automatically.
>> > +    # element - (object) HTML dom element
>> > +    # attribute - (string) name of the data attribute without the
>> "data-" prefix.
>> > +    getDataAttributeAsObject = (element, attribute) ->
>> > +
>> > +#if jquery
>> > +      value = $(element).data(attribute)
>> > +#elseif prototype
>> > +      value = JSON.parse($(element).readAttribute('data-' +
>> attribute))
>> > +      if value isnt null
>> > +        value = JSON.parse(value)
>> > +      else
>> > +        value = {}
>> > +#endif
>> >
>> >      # onDocument() is used to add an event handler to the document
>> object; this is used
>> >      # for global (or default) handlers.
>> > @@ -919,5 +973,7 @@ define ["underscore", "./utils", "./events",
>> "jquery"],
>> >      body: wrapElement document.body
>> >
>> >      scanner: scanner
>> > +
>> > +    getEventUrl : getEventUrl
>> >
>> >    return exports
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/app1/PublishEventDemo.tml
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/app1/PublishEventDemo.tml
>> b/tapestry-core/src/test/app1/PublishEventDemo.tml
>> > new file mode 100644
>> > index 0000000..43c2bd7
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/app1/PublishEventDemo.tml
>> > @@ -0,0 +1,7 @@
>> > +<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.
>> xsd"
>> > +          xmlns:p="tapestry:parameter">
>> > +    <div id="page">
>> > +           <t:PublishEventDemoComponent/>
>> > +           <t:PublishEventDemoComponent2/>
>> > +    </div>
>> > +</t:border>
>> > \ No newline at end of file
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/java/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/components/PublishEventDemoComponent.java
>> b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/components/PublishEventDemoComponent.java
>> > new file mode 100644
>> > index 0000000..b8054f9
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/components/PublishEventDemoComponent.java
>> > @@ -0,0 +1,32 @@
>> > +// Licensed 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.tapestry5.integration.app1.components;
>> > +
>> > +import org.apache.tapestry5.annotations.OnEvent;
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> > +import org.apache.tapestry5.json.JSONObject;
>> > +
>> > +public class PublishEventDemoComponent
>> > +{
>> > +    @OnEvent("answer")
>> > +    @PublishEvent
>> > +    JSONObject answer() {
>> > +        return new JSONObject("origin", "component");
>> > +    }
>> > +
>> > +    @PublishEvent
>> > +    JSONObject onAction()
>> > +    {
>> > +        return new JSONObject("origin", "component");
>> > +    }
>> > +
>> > +}
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/java/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent2.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/components/PublishEventDemoComponent2.java
>> b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/components/PublishEventDemoComponent2.java
>> > new file mode 100644
>> > index 0000000..7771557
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/components/PublishEventDemoComponent2.java
>> > @@ -0,0 +1,32 @@
>> > +// Licensed 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.tapestry5.integration.app1.components;
>> > +
>> > +import org.apache.tapestry5.annotations.OnEvent;
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> > +import org.apache.tapestry5.json.JSONObject;
>> > +
>> > +public class PublishEventDemoComponent2
>> > +{
>> > +    @PublishEvent
>> > +    JSONObject onAction()
>> > +    {
>> > +        return new JSONObject("origin", "component");
>> > +    }
>> > +
>> > +    @OnEvent("answer")
>> > +    @PublishEvent
>> > +    JSONObject answer() {
>> > +        return new JSONObject("origin", "component");
>> > +    }
>> > +
>> > +}
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/java/org/apache/tapestry5/
>> integration/app1/pages/Index.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
>> b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/pages/Index.java
>> > index d7d76d6..e78a874 100644
>> > --- a/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/pages/Index.java
>> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/pages/Index.java
>> > @@ -57,32 +57,35 @@ public class Index
>> >
>> >      private static final List<Item> ITEMS = CollectionFactory
>> >              .newList(
>> > -                    new Item("ZoneFormDemo", "Zone Form Decoration",
>> "Fields inside an Ajax-updatd Form are still decorated properly."),
>> > +
>> > +                  new Item("PublishEventDemo", "@PublishEvent Demo",
>> "Publishing server-side events to client-side code (JavaScript)"),
>> > +
>> > +//                    new Item("ZoneFormDemo", "Zone Form Decoration",
>> "Fields inside an Ajax-updatd Form are still decorated properly."),
>> >
>> > -                    new Item("AjaxValidationDemo", "Ajax Validation",
>> "Demonstrated proper integration of server-side validation and client-side
>> field decoration."),
>> > +//                    new Item("AjaxValidationDemo", "Ajax
>> Validation", "Demonstrated proper integration of server-side validation and
>> client-side field decoration."),
>> >
>> >                      new Item("OverrideEventHandlerDemo", "Event
>> Handler Override Demo", "Event Handler methods overridden by sub-classes
>> invoke base-class correctly."),
>> >
>> > -                    new Item("LogoSubclass", "Base class Assets in
>> sub-classes", "Assets are resolved for the parent class if that's where the
>> annotations are."),
>> > +//                    new Item("LogoSubclass", "Base class Assets in
>> sub-classes", "Assets are resolved for the parent class if that's where the
>> annotations are."),
>> >
>> >                      new Item("MissingRequiredARP", "Missing Query
>> Parameter for @ActivationRequestParameter", "Activating a page with a
>> required @ActivationRequestParameter, but no matching query parameter, is
>> an error."),
>> >
>> > -                    new Item("DateFieldValidationDemo", "DateField
>> Validation Demo",
>> > -                            "Use of DateField component when client
>> validation is disabled."),
>> > +//                    new Item("DateFieldValidationDemo", "DateField
>> Validation Demo",
>> > +//                            "Use of DateField component when client
>> validation is disabled."),
>> >
>> >                      new Item("MixinParameters54", "Strict Mixin
>> Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the
>> mixin id."),
>> >
>> > -                    new Item("AsyncDemo", "Async Links and Forms
>> Demo", "Async (XHR) Updates without a containing Zone."),
>> > +//                    new Item("AsyncDemo", "Async Links and Forms
>> Demo", "Async (XHR) Updates without a containing Zone."),
>> >
>> >                      new Item("FormCancelActionDemo", "Form Cancel
>> Action Demo", "FormSupport.addCancel() support"),
>> >
>> >                      new Item("AjaxRadioDemo", "Ajax Radio Demo",
>> "Radio components inside an Ajax form"),
>> >
>> > -                    new Item("TimeIntervalDemo", "TimeInterval Demo",
>> "Interval component, based on Moment.js"),
>> > +//                    new Item("TimeIntervalDemo", "TimeInterval
>> Demo", "Interval component, based on Moment.js"),
>> >
>> > -                    new Item("LocalDateDemo", "LocalDate Demo",
>> "LocalDate component, based on Moment.js"),
>> > +//                    new Item("LocalDateDemo", "LocalDate Demo",
>> "LocalDate component, based on Moment.js"),
>> >
>> > -                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure
>> an empty If can still render."),
>> > +//                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure
>> an empty If can still render."),
>> >
>> >                      new Item("MissingAssetDemo", "Missing Asset Demo",
>> "Error when injecting an asset that does not exist."),
>> >
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/java/org/apache/tapestry5/
>> integration/app1/pages/PublishEventDemo.java
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/pages/PublishEventDemo.java b/tapestry-core/src/test/java/
>> org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
>> > new file mode 100644
>> > index 0000000..1876de5
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integrati
>> on/app1/pages/PublishEventDemo.java
>> > @@ -0,0 +1,49 @@
>> > +// Copyright 2016 The Apache Software Foundation
>> > +//
>> > +// Licensed 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.
>> > +
>> > +// Licensed 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.tapestry5.integration.app1.pages;
>> > +
>> > +import org.apache.tapestry5.annotations.Import;
>> > +import org.apache.tapestry5.annotations.OnEvent;
>> > +import org.apache.tapestry5.annotations.PublishEvent;
>> > +import org.apache.tapestry5.json.JSONObject;
>> > +
>> > +@Import(stack = "core", library = "PublishEventDemo.js")
>> > +public class PublishEventDemo
>> > +{
>> > +
>> > +    @PublishEvent
>> > +    JSONObject onAction()
>> > +    {
>> > +        return new JSONObject("origin", "page");
>> > +    }
>> > +
>> > +    @OnEvent("answer")
>> > +    @PublishEvent
>> > +    JSONObject answer() {
>> > +        return new JSONObject("origin", "page");
>> > +    }
>> > +
>> > +}
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
>> b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
>> > new file mode 100644
>> > index 0000000..20c0406
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/resources/META-INF/assets/PublishEv
>> entDemo.js
>> > @@ -0,0 +1,11 @@
>> > +require(["t5/core/dom", "t5/core/ajax", "jquery"], function (dom,
>> ajax, $) {
>> > +
>> > +    $(document).ready(function() {
>> > +        console.log('dom.getEventURL()   : ' +
>> dom.getEventUrl('answer', document.getElementById("page")));
>> > +        console.log('dom.getEventURL() 1 : ' +
>> dom.getEventUrl('answer', document.getElementById("compo
>> nentParagraph")));
>> > +        console.log('dom.getEventURL() 2 : ' +
>> dom.getEventUrl('answer', document.getElementById("compo
>> nentParagraph2")));
>> > +        console.log('dom.getEventURL() 3 : ' +
>> dom.getEventUrl('answer', document.getElementById("compo
>> nentParagraph3")));
>> > +    });
>> > +
>> > +});
>> > +
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/resources/org/apache/tapestry5
>> /integration/app1/components/PublishEventDemoComponent.tml
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent.tml
>> b/tapestry-core/src/test/resources/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent.tml
>> > new file mode 100644
>> > index 0000000..2d2b5f1
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/resources/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent.tml
>> > @@ -0,0 +1,3 @@
>> > +<div id="component" xmlns:t="http://tapestry.apach
>> e.org/schema/tapestry_5_0_0.xsd">
>> > +       <p id="componentParagraph">I'm a component</p>
>> > +</div>
>> >
>> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca
>> 30b/tapestry-core/src/test/resources/org/apache/tapestry5
>> /integration/app1/components/PublishEventDemoComponent2.tml
>> > ----------------------------------------------------------------------
>> > diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent2.tml
>> b/tapestry-core/src/test/resources/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent2.tml
>> > new file mode 100644
>> > index 0000000..b10d020
>> > --- /dev/null
>> > +++ b/tapestry-core/src/test/resources/org/apache/tapestry5/
>> integration/app1/components/PublishEventDemoComponent2.tml
>> > @@ -0,0 +1,4 @@
>> > +<t:container xmlns:t="http://tapestry.apach
>> e.org/schema/tapestry_5_0_0.xsd">
>> > +       <p id="componentParagraph2">I'm another component</p>
>> > +       <p id="componentParagraph3">I'm another component</p>
>> > +</t:container>
>> >
>>
>>
>>
>> --
>> Massimo Lusetti
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
>> For additional commands, e-mail: dev-help@tapestry.apache.org
>>
>>
>

Re: tapestry-5 git commit: TAP5-2225

Posted by Dimitris Zenios <di...@gmail.com>.
My only concern is with
/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee

+#if jquery
+      value = $(element).data(attribute)
+#elseif prototype
+      value = JSON.parse($(element).readAttribute('data-' + attribute))
+      if value isnt null
+        value = JSON.parse(value)
+      else
+        value = {}
+#endif

What happens when not jquery neither prototype is used.Instead it should
use the dom api.

Shall i post this to the issue itself?

On Sun, Mar 19, 2017 at 8:09 PM, Massimo Lusetti <ml...@gmail.com> wrote:

> Cool and long awaited feature.
>
> On Sun, Mar 19, 2017 at 5:08 PM,  <th...@apache.org> wrote:
> > Repository: tapestry-5
> > Updated Branches:
> >   refs/heads/master aaa0d550a -> 6b4ca30b8
> >
> >
> > TAP5-2225
> >
> > Create client-side API to call a component's event handler methods
> >
> > Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
> > Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/
> 6b4ca30b
> > Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6b4ca30b
> > Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6b4ca30b
> >
> > Branch: refs/heads/master
> > Commit: 6b4ca30b83bed396006107f5008a3781eee1db86
> > Parents: aaa0d55
> > Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
> > Authored: Sun Mar 19 13:07:00 2017 -0300
> > Committer: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
> > Committed: Sun Mar 19 13:07:00 2017 -0300
> >
> > ----------------------------------------------------------------------
> >  .../org/apache/tapestry5/TapestryConstants.java |  11 ++
> >  .../tapestry5/annotations/PublishEvent.java     |  50 ++++++
> >  .../corelib/mixins/PublishServerSideEvents.java | 151
> +++++++++++++++++++
> >  .../tapestry5/internal/InternalConstants.java   |  17 +++
> >  .../internal/transform/OnEventWorker.java       |  54 ++++++-
> >  .../org/apache/tapestry5/t5-core-dom.coffee     |  56 +++++++
> >  .../src/test/app1/PublishEventDemo.tml          |   7 +
> >  .../components/PublishEventDemoComponent.java   |  32 ++++
> >  .../components/PublishEventDemoComponent2.java  |  32 ++++
> >  .../tapestry5/integration/app1/pages/Index.java |  21 +--
> >  .../app1/pages/PublishEventDemo.java            |  49 ++++++
> >  .../META-INF/assets/PublishEventDemo.js         |  11 ++
> >  .../components/PublishEventDemoComponent.tml    |   3 +
> >  .../components/PublishEventDemoComponent2.tml   |   4 +
> >  14 files changed, 482 insertions(+), 16 deletions(-)
> > ----------------------------------------------------------------------
> >
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/
> tapestry5/TapestryConstants.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> > index ad1495a..cab8c46 100644
> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/
> TapestryConstants.java
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/
> TapestryConstants.java
> > @@ -12,6 +12,7 @@
> >
> >  package org.apache.tapestry5;
> >
> > +import org.apache.tapestry5.annotations.PublishEvent;
> >  import org.apache.tapestry5.internal.structure.PageResetListener;
> >  import org.apache.tapestry5.services.ComponentEventLinkEncoder;
> >
> > @@ -68,4 +69,14 @@ public class TapestryConstants
> >       */
> >      public static final String DISABLE_JAVASCRIPT_MINIMIZATION =
> "tapestry.disable-javascript-minimization";
> >
> > +    /**
> > +     * Name of the HTML data attribute which contains information about
> component events
> > +     * published by using the {@linkplain PublishEvent} annotation
> > +     * in a component event handler method.
> > +     *
> > +     * @see PublishEvent
> > +     * @since 5.4.2
> > +     */
> > +    public static final String COMPONENT_EVENTS_ATTRIBUTE_NAME =
> "data-component-events";
> > +
> >  }
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/
> PublishEvent.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/
> annotations/PublishEvent.java
> > new file mode 100644
> > index 0000000..18239d4
> > --- /dev/null
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/
> annotations/PublishEvent.java
> > @@ -0,0 +1,50 @@
> > +// Licensed 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.tapestry5.annotations;
> > +
> > +import static java.lang.annotation.RetentionPolicy.RUNTIME;
> > +import static org.apache.tapestry5.ioc.annotations.
> AnnotationUseContext.COMPONENT;
> > +import static org.apache.tapestry5.ioc.annotations.
> AnnotationUseContext.PAGE;
> > +
> > +import java.lang.annotation.Documented;
> > +import java.lang.annotation.ElementType;
> > +import java.lang.annotation.Retention;
> > +import java.lang.annotation.Target;
> > +
> > +import org.apache.tapestry5.ioc.annotations.UseWith;
> > +
> > +/**
> > + * Marks an event handler method to be published as an event to be
> called in JavaScript
> > + * through the <code>t5/core/triggerServerEvent</code> function.
> > + *
> > + * The event information is stored in JSON format inside the
> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}
> attribute.
> > + *
> > + * When used in a component method, the component must render at least
> one element,
> > + * and that's what get the
> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}
> attribute above.
> > + * If it doesn't, an exception will be thrown.
> > + *
> > + * When used in a page method, the page must render an &lt;body&gt;
> element,
> > + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}.
> If it doesn't,
> > + * an exception will be thrown.
> > + *
> > + * @since 5.4.2
> > + */
> > +@Target(ElementType.METHOD)
> > +@Retention(RUNTIME)
> > +@Documented
> > +@UseWith({ COMPONENT, PAGE })
> > +public @interface PublishEvent
> > +{
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/
> PublishServerSideEvents.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/
> mixins/PublishServerSideEvents.java b/tapestry-core/src/main/java/
> org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
> > new file mode 100644
> > index 0000000..3d73746
> > --- /dev/null
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/
> PublishServerSideEvents.java
> > @@ -0,0 +1,151 @@
> > +// Licensed 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.tapestry5.corelib.mixins;
> > +
> > +import org.apache.tapestry5.ComponentResources;
> > +import org.apache.tapestry5.MarkupWriter;
> > +import org.apache.tapestry5.MarkupWriterListener;
> > +import org.apache.tapestry5.TapestryConstants;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.dom.Element;
> > +import org.apache.tapestry5.internal.InternalConstants;
> > +import org.apache.tapestry5.ioc.annotations.Inject;
> > +import org.apache.tapestry5.json.JSONArray;
> > +import org.apache.tapestry5.json.JSONObject;
> > +import org.apache.tapestry5.model.ComponentModel;
> > +
> > +/**
> > + * Tapestry internal mixin used to implement the {@link PublishEvent}
> event logic. Don't use directly.
> > + */
> > +public class PublishServerSideEvents
> > +{
> > +
> > +    private static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY
> = InternalConstants.PUBLISH_COMPONENT_EVENTS_URL_PROPERTY;
> > +    private static final String COMPONENT_EVENTS_ATTRIBUTE_NAME =
> TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME;
> > +
> > +    @Inject
> > +    private ComponentResources resources;
> > +
> > +    void beginRender(final MarkupWriter writer) {
> > +
> > +        final Element element = writer.getElement();
> > +
> > +        // When the component is actually a page, nothing was rendered
> yet.
> > +        // The listener we add here will add the events attribute to
> the <body> element
> > +        // later
> > +        if (element == null) {
> > +            writer.addListener(new BodyElementListener(writer));
> > +        }
> > +        else {
> > +            writer.addListener(new DelayedListener(writer));
> > +        }
> > +
> > +    }
> > +
> > +    private void addEventsAttribute(final Element element)
> > +    {
> > +
> > +        if (element == null)
> > +        {
> > +            throw new IllegalStateException("@PublishEvent used inside
> a page which didn't generate a <body> element");
> > +        }
> > +
> > +        final ComponentResources containerResources = resources.
> getContainerResources();
> > +        final ComponentModel componentModel = containerResources.
> getComponentModel();
> > +        final String metaValue = componentModel.getMeta(
> InternalConstants.PUBLISH_COMPONENT_EVENTS_META);
> > +        final JSONArray componentEvents = new JSONArray(metaValue);
> > +        final JSONObject events = new JSONObject();
> > +        final String existingValue = element.getAttribute(
> COMPONENT_EVENTS_ATTRIBUTE_NAME);
> > +
> > +        if (existingValue != null)
> > +        {
> > +            final JSONObject existing = new JSONObject(existingValue);
> > +            for (String key : existing.keys()) {
> > +                events.put(key, existing.get(key));
> > +            }
> > +        }
> > +
> > +        for (int i = 0; i < componentEvents.length(); i++)
> > +        {
> > +            final String eventName = componentEvents.getString(i);
> > +            JSONObject event = new JSONObject();
> > +            event.put(PUBLISH_COMPONENT_EVENTS_URL_PROPERTY,
> containerResources.createEventLink(eventName).toString());
> > +            events.put(eventName, event);
> > +        }
> > +
> > +        element.forceAttributes(TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME,
> events.toString());
> > +    }
> > +
> > +    final private class DelayedListener implements MarkupWriterListener
> {
> > +
> > +        private MarkupWriter writer;
> > +
> > +        private Element element;
> > +
> > +        public DelayedListener(MarkupWriter writer)
> > +        {
> > +            super();
> > +            this.writer = writer;
> > +        }
> > +
> > +        @Override
> > +        public void elementDidStart(Element element)
> > +        {
> > +            // Store first element generated by rendering the component
> > +            if (this.element == null)
> > +            {
> > +                this.element = element;
> > +            }
> > +        }
> > +
> > +        @Override
> > +        public void elementDidEnd(Element element)
> > +        {
> > +            if (this.element == null)
> > +            {
> > +                throw new IllegalStateException("@PublishEvent used
> inside a component which didn't generate any HTML elements");
> > +            }
> > +            addEventsAttribute(this.element);
> > +            writer.removeListener(this);
> > +        }
> > +
> > +    }
> > +
> > +    final private class BodyElementListener implements
> MarkupWriterListener {
> > +
> > +        private MarkupWriter writer;
> > +
> > +        public BodyElementListener(MarkupWriter writer)
> > +        {
> > +            super();
> > +            this.writer = writer;
> > +        }
> > +
> > +        @Override
> > +        public void elementDidStart(Element element)
> > +        {
> > +            if (element.getName().equals("body"))
> > +            {
> > +                addEventsAttribute(element);
> > +                writer.removeListener(this);
> > +            }
> > +        }
> > +
> > +        @Override
> > +        public void elementDidEnd(Element element)
> > +        {
> > +        }
> > +
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > index 32b80a6..8370ae0 100644
> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> InternalConstants.java
> > @@ -13,8 +13,10 @@
> >  package org.apache.tapestry5.internal;
> >
> >  import org.apache.tapestry5.ContentType;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> >  import org.apache.tapestry5.dom.MarkupModel;
> >  import org.apache.tapestry5.ioc.util.TimeInterval;
> > +import org.apache.tapestry5.model.ComponentModel;
> >  import org.apache.tapestry5.services.javascript.JavaScriptStack;
> >
> >  public final class InternalConstants
> > @@ -213,4 +215,19 @@ public final class InternalConstants
> >       * @since 5.4
> >       */
> >      public static final ContentType JAVASCRIPT_CONTENT_TYPE = new
> ContentType("text/javascript");
> > +
> > +    /**
> > +     * Name of the {@linkplain ComponentModel} metadata key whiche
> stores the {@linkplain PublishEvent}
> > +     * data.
> > +     * @since 5.4.2
> > +     */
> > +    public static final String PUBLISH_COMPONENT_EVENTS_META =
> "meta.publish-component-events";
> > +
> > +    /**
> > +     * Name of the JSONObject key name which holds the name of the
> event to be published.
> > +     *
> > +     * @since 5.4.2
> > +     */
> > +    public static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY =
> "url";
> > +
> >  }
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/java/org/apache/
> tapestry5/internal/transform/OnEventWorker.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
> b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> transform/OnEventWorker.java
> > index fb4502b..fdb2dc9 100644
> > --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> transform/OnEventWorker.java
> > +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/
> transform/OnEventWorker.java
> > @@ -12,15 +12,23 @@
> >
> >  package org.apache.tapestry5.internal.transform;
> >
> > +import java.lang.reflect.Array;
> > +import java.util.Arrays;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> >  import org.apache.tapestry5.ComponentResources;
> >  import org.apache.tapestry5.EventContext;
> >  import org.apache.tapestry5.ValueEncoder;
> >  import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> >  import org.apache.tapestry5.annotations.RequestParameter;
> > +import org.apache.tapestry5.corelib.mixins.PublishServerSideEvents;
> >  import org.apache.tapestry5.func.F;
> >  import org.apache.tapestry5.func.Flow;
> >  import org.apache.tapestry5.func.Mapper;
> >  import org.apache.tapestry5.func.Predicate;
> > +import org.apache.tapestry5.internal.InternalConstants;
> >  import org.apache.tapestry5.internal.services.ComponentClassCache;
> >  import org.apache.tapestry5.ioc.OperationTracker;
> >  import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> > @@ -28,8 +36,19 @@ import org.apache.tapestry5.ioc.
> internal.util.InternalUtils;
> >  import org.apache.tapestry5.ioc.internal.util.TapestryException;
> >  import org.apache.tapestry5.ioc.util.ExceptionUtils;
> >  import org.apache.tapestry5.ioc.util.UnknownValueException;
> > +import org.apache.tapestry5.json.JSONArray;
> >  import org.apache.tapestry5.model.MutableComponentModel;
> > -import org.apache.tapestry5.plastic.*;
> > +import org.apache.tapestry5.plastic.Condition;
> > +import org.apache.tapestry5.plastic.InstructionBuilder;
> > +import org.apache.tapestry5.plastic.InstructionBuilderCallback;
> > +import org.apache.tapestry5.plastic.LocalVariable;
> > +import org.apache.tapestry5.plastic.LocalVariableCallback;
> > +import org.apache.tapestry5.plastic.MethodAdvice;
> > +import org.apache.tapestry5.plastic.MethodDescription;
> > +import org.apache.tapestry5.plastic.MethodInvocation;
> > +import org.apache.tapestry5.plastic.PlasticClass;
> > +import org.apache.tapestry5.plastic.PlasticField;
> > +import org.apache.tapestry5.plastic.PlasticMethod;
> >  import org.apache.tapestry5.runtime.ComponentEvent;
> >  import org.apache.tapestry5.runtime.Event;
> >  import org.apache.tapestry5.runtime.PageLifecycleListener;
> > @@ -39,11 +58,6 @@ import org.apache.tapestry5.services.
> ValueEncoderSource;
> >  import org.apache.tapestry5.services.transform.
> ComponentClassTransformWorker2;
> >  import org.apache.tapestry5.services.transform.TransformationSupport;
> >
> > -import java.lang.reflect.Array;
> > -import java.util.Arrays;
> > -import java.util.List;
> > -import java.util.Map;
> > -
> >  /**
> >   * Provides implementations of the
> >   * {@link org.apache.tapestry5.runtime.Component#
> dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
> > @@ -151,6 +165,8 @@ public class OnEventWorker implements
> ComponentClassTransformWorker2
> >          int minContextValues = 0;
> >
> >          boolean handleActivationEventContext = false;
> > +
> > +        final PublishEvent publishEvent;
> >
> >          EventHandlerMethod(PlasticMethod method)
> >          {
> > @@ -165,6 +181,8 @@ public class OnEventWorker implements
> ComponentClassTransformWorker2
> >
> >              eventType = extractEventType(methodName, onEvent);
> >              componentId = extractComponentId(methodName, onEvent);
> > +
> > +            publishEvent = method.getAnnotation(PublishEvent.class);
> >          }
> >
> >          void buildMatchAndInvocation(InstructionBuilder builder, final
> LocalVariable resultVariable)
> > @@ -343,9 +361,31 @@ public class OnEventWorker implements
> ComponentClassTransformWorker2
> >          implementDispatchMethod(plasticClass, isRoot, model,
> eventHandlerMethods);
> >
> >          addComponentIdValidationLogicOnPageLoad(plasticClass,
> eventHandlerMethods);
> > +
> > +        addPublishEventInfo(eventHandlerMethods, model);
> > +    }
> > +
> > +    private void addPublishEventInfo(Flow<EventHandlerMethod>
> eventHandlerMethods,
> > +            MutableComponentModel model)
> > +    {
> > +        JSONArray publishEvents = new JSONArray();
> > +        for (EventHandlerMethod eventHandlerMethod :
> eventHandlerMethods)
> > +        {
> > +            if (eventHandlerMethod.publishEvent != null)
> > +            {
> > +                publishEvents.put(eventHandlerMethod.eventType.
> toLowerCase());
> > +            }
> > +        }
> > +
> > +        // If we do have events to publish, we apply the mixin and pass
> > +        // event information to it.
> > +        if (publishEvents.length() > 0) {
> > +            model.addMixinClassName(PublishServerSideEvents.class.getName(),
> "after:*");
> > +            model.setMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META,
> publishEvents.toString());
> > +        }
> >      }
> >
> > -    private void addComponentIdValidationLogicOnPageLoad(PlasticClass
> plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
> > +       private void addComponentIdValidationLogicOnPageLoad(PlasticClass
> plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
> >      {
> >          ComponentIdValidator[] validators =
> extractComponentIdValidators(eventHandlerMethods);
> >
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/
> tapestry5/t5-core-dom.coffee
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee b/tapestry-core/src/main/
> preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
> > index 9d419bc..9b975d4 100644
> > --- a/tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee
> > +++ b/tapestry-core/src/main/preprocessed-coffeescript/org/
> apache/tapestry5/t5-core-dom.coffee
> > @@ -906,6 +906,60 @@ define ["underscore", "./utils", "./events",
> "jquery"],
> >        events = utils.split events
> >  #endif
> >        onevent elements, events, match, handler
> > +
> > +    # Returns the URL of a component event based on its name and an
> optional element
> > +    # or null if the event information is not found. When the element
> isn't passed
> > +    # or it's null, the event data is taken from the <body> element.
> > +    #
> > +    # * eventName - (string) name of the component event
> > +    # * element - (object) HTML DOM element to be used as the begining
> of the event data search. Optional.
> > +    getEventUrl = (eventName, element) ->
> > +
> > +      if not (eventName?)
> > +        throw 'dom.getEventUrl: the eventName parameter cannot be null'
> > +
> > +      if not _.isString eventName
> > +        throw 'dom.getEventUrl: the eventName parameter should be a
> string'
> > +
> > +      eventName = eventName.toLowerCase()
> > +
> > +      if element is null
> > +        element = document.getElementsByTagName('body')[0]
> > +
> > +      # Look for event data in itself first, then in the preceding
> siblings
> > +      # if not found
> > +      url = null
> > +
> > +      while not url? and element.previousElementSibling?
> > +        data = getDataAttributeAsObject(element, 'component-events')
> > +        url = data?[eventName]?.url
> > +        element = element.previousElementSibling
> > +
> > +      if not url?
> > +
> > +        # Look at parent elements recursively
> > +        while not url? and element.parentElement?
> > +          data = getDataAttributeAsObject(element, 'component-events')
> > +          url = data?[eventName]?.url
> > +          element = element.parentElement;
> > +
> > +      return url;
> > +
> > +    # Returns the value of a given data attribute as an object.
> > +    # The "data-" prefix is added automatically.
> > +    # element - (object) HTML dom element
> > +    # attribute - (string) name of the data attribute without the
> "data-" prefix.
> > +    getDataAttributeAsObject = (element, attribute) ->
> > +
> > +#if jquery
> > +      value = $(element).data(attribute)
> > +#elseif prototype
> > +      value = JSON.parse($(element).readAttribute('data-' + attribute))
> > +      if value isnt null
> > +        value = JSON.parse(value)
> > +      else
> > +        value = {}
> > +#endif
> >
> >      # onDocument() is used to add an event handler to the document
> object; this is used
> >      # for global (or default) handlers.
> > @@ -919,5 +973,7 @@ define ["underscore", "./utils", "./events",
> "jquery"],
> >      body: wrapElement document.body
> >
> >      scanner: scanner
> > +
> > +    getEventUrl : getEventUrl
> >
> >    return exports
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/app1/PublishEventDemo.tml
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/app1/PublishEventDemo.tml
> b/tapestry-core/src/test/app1/PublishEventDemo.tml
> > new file mode 100644
> > index 0000000..43c2bd7
> > --- /dev/null
> > +++ b/tapestry-core/src/test/app1/PublishEventDemo.tml
> > @@ -0,0 +1,7 @@
> > +<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd
> "
> > +          xmlns:p="tapestry:parameter">
> > +    <div id="page">
> > +           <t:PublishEventDemoComponent/>
> > +           <t:PublishEventDemoComponent2/>
> > +    </div>
> > +</t:border>
> > \ No newline at end of file
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent.java
> > new file mode 100644
> > index 0000000..b8054f9
> > --- /dev/null
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent.java
> > @@ -0,0 +1,32 @@
> > +// Licensed 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.tapestry5.integration.app1.components;
> > +
> > +import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.json.JSONObject;
> > +
> > +public class PublishEventDemoComponent
> > +{
> > +    @OnEvent("answer")
> > +    @PublishEvent
> > +    JSONObject answer() {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +    @PublishEvent
> > +    JSONObject onAction()
> > +    {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent2.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent2.java
> > new file mode 100644
> > index 0000000..7771557
> > --- /dev/null
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/components/PublishEventDemoComponent2.java
> > @@ -0,0 +1,32 @@
> > +// Licensed 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.tapestry5.integration.app1.components;
> > +
> > +import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.json.JSONObject;
> > +
> > +public class PublishEventDemoComponent2
> > +{
> > +    @PublishEvent
> > +    JSONObject onAction()
> > +    {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +    @OnEvent("answer")
> > +    @PublishEvent
> > +    JSONObject answer() {
> > +        return new JSONObject("origin", "component");
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/pages/Index.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/Index.java b/tapestry-core/src/test/java/
> org/apache/tapestry5/integration/app1/pages/Index.java
> > index d7d76d6..e78a874 100644
> > --- a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/Index.java
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/Index.java
> > @@ -57,32 +57,35 @@ public class Index
> >
> >      private static final List<Item> ITEMS = CollectionFactory
> >              .newList(
> > -                    new Item("ZoneFormDemo", "Zone Form Decoration",
> "Fields inside an Ajax-updatd Form are still decorated properly."),
> > +
> > +                  new Item("PublishEventDemo", "@PublishEvent Demo",
> "Publishing server-side events to client-side code (JavaScript)"),
> > +
> > +//                    new Item("ZoneFormDemo", "Zone Form Decoration",
> "Fields inside an Ajax-updatd Form are still decorated properly."),
> >
> > -                    new Item("AjaxValidationDemo", "Ajax Validation",
> "Demonstrated proper integration of server-side validation and client-side
> field decoration."),
> > +//                    new Item("AjaxValidationDemo", "Ajax Validation",
> "Demonstrated proper integration of server-side validation and client-side
> field decoration."),
> >
> >                      new Item("OverrideEventHandlerDemo", "Event
> Handler Override Demo", "Event Handler methods overridden by sub-classes
> invoke base-class correctly."),
> >
> > -                    new Item("LogoSubclass", "Base class Assets in
> sub-classes", "Assets are resolved for the parent class if that's where the
> annotations are."),
> > +//                    new Item("LogoSubclass", "Base class Assets in
> sub-classes", "Assets are resolved for the parent class if that's where the
> annotations are."),
> >
> >                      new Item("MissingRequiredARP", "Missing Query
> Parameter for @ActivationRequestParameter", "Activating a page with a
> required @ActivationRequestParameter, but no matching query parameter, is
> an error."),
> >
> > -                    new Item("DateFieldValidationDemo", "DateField
> Validation Demo",
> > -                            "Use of DateField component when client
> validation is disabled."),
> > +//                    new Item("DateFieldValidationDemo", "DateField
> Validation Demo",
> > +//                            "Use of DateField component when client
> validation is disabled."),
> >
> >                      new Item("MixinParameters54", "Strict Mixin
> Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the
> mixin id."),
> >
> > -                    new Item("AsyncDemo", "Async Links and Forms Demo",
> "Async (XHR) Updates without a containing Zone."),
> > +//                    new Item("AsyncDemo", "Async Links and Forms
> Demo", "Async (XHR) Updates without a containing Zone."),
> >
> >                      new Item("FormCancelActionDemo", "Form Cancel
> Action Demo", "FormSupport.addCancel() support"),
> >
> >                      new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio
> components inside an Ajax form"),
> >
> > -                    new Item("TimeIntervalDemo", "TimeInterval Demo",
> "Interval component, based on Moment.js"),
> > +//                    new Item("TimeIntervalDemo", "TimeInterval Demo",
> "Interval component, based on Moment.js"),
> >
> > -                    new Item("LocalDateDemo", "LocalDate Demo",
> "LocalDate component, based on Moment.js"),
> > +//                    new Item("LocalDateDemo", "LocalDate Demo",
> "LocalDate component, based on Moment.js"),
> >
> > -                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure an
> empty If can still render."),
> > +//                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure
> an empty If can still render."),
> >
> >                      new Item("MissingAssetDemo", "Missing Asset Demo",
> "Error when injecting an asset that does not exist."),
> >
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/java/org/apache/
> tapestry5/integration/app1/pages/PublishEventDemo.java
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/PublishEventDemo.java
> b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/
> PublishEventDemo.java
> > new file mode 100644
> > index 0000000..1876de5
> > --- /dev/null
> > +++ b/tapestry-core/src/test/java/org/apache/tapestry5/
> integration/app1/pages/PublishEventDemo.java
> > @@ -0,0 +1,49 @@
> > +// Copyright 2016 The Apache Software Foundation
> > +//
> > +// Licensed 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.
> > +
> > +// Licensed 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.tapestry5.integration.app1.pages;
> > +
> > +import org.apache.tapestry5.annotations.Import;
> > +import org.apache.tapestry5.annotations.OnEvent;
> > +import org.apache.tapestry5.annotations.PublishEvent;
> > +import org.apache.tapestry5.json.JSONObject;
> > +
> > +@Import(stack = "core", library = "PublishEventDemo.js")
> > +public class PublishEventDemo
> > +{
> > +
> > +    @PublishEvent
> > +    JSONObject onAction()
> > +    {
> > +        return new JSONObject("origin", "page");
> > +    }
> > +
> > +    @OnEvent("answer")
> > +    @PublishEvent
> > +    JSONObject answer() {
> > +        return new JSONObject("origin", "page");
> > +    }
> > +
> > +}
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/resources/META-INF/
> assets/PublishEventDemo.js
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> > new file mode 100644
> > index 0000000..20c0406
> > --- /dev/null
> > +++ b/tapestry-core/src/test/resources/META-INF/assets/
> PublishEventDemo.js
> > @@ -0,0 +1,11 @@
> > +require(["t5/core/dom", "t5/core/ajax", "jquery"], function (dom, ajax,
> $) {
> > +
> > +    $(document).ready(function() {
> > +        console.log('dom.getEventURL()   : ' +
> dom.getEventUrl('answer', document.getElementById("page")));
> > +        console.log('dom.getEventURL() 1 : ' +
> dom.getEventUrl('answer', document.getElementById("componentParagraph")));
> > +        console.log('dom.getEventURL() 2 : ' +
> dom.getEventUrl('answer', document.getElementById("
> componentParagraph2")));
> > +        console.log('dom.getEventURL() 3 : ' +
> dom.getEventUrl('answer', document.getElementById("
> componentParagraph3")));
> > +    });
> > +
> > +});
> > +
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/
> components/PublishEventDemoComponent.tml
> > new file mode 100644
> > index 0000000..2d2b5f1
> > --- /dev/null
> > +++ b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> > @@ -0,0 +1,3 @@
> > +<div id="component" xmlns:t="http://tapestry.
> apache.org/schema/tapestry_5_0_0.xsd">
> > +       <p id="componentParagraph">I'm a component</p>
> > +</div>
> >
> > http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/
> 6b4ca30b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> > ----------------------------------------------------------------------
> > diff --git a/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/
> components/PublishEventDemoComponent2.tml
> > new file mode 100644
> > index 0000000..b10d020
> > --- /dev/null
> > +++ b/tapestry-core/src/test/resources/org/apache/
> tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> > @@ -0,0 +1,4 @@
> > +<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_
> 0_0.xsd">
> > +       <p id="componentParagraph2">I'm another component</p>
> > +       <p id="componentParagraph3">I'm another component</p>
> > +</t:container>
> >
>
>
>
> --
> Massimo Lusetti
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: dev-help@tapestry.apache.org
>
>

Re: tapestry-5 git commit: TAP5-2225

Posted by Massimo Lusetti <ml...@gmail.com>.
Cool and long awaited feature.

On Sun, Mar 19, 2017 at 5:08 PM,  <th...@apache.org> wrote:
> Repository: tapestry-5
> Updated Branches:
>   refs/heads/master aaa0d550a -> 6b4ca30b8
>
>
> TAP5-2225
>
> Create client-side API to call a component's event handler methods
>
> Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
> Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/6b4ca30b
> Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/6b4ca30b
> Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/6b4ca30b
>
> Branch: refs/heads/master
> Commit: 6b4ca30b83bed396006107f5008a3781eee1db86
> Parents: aaa0d55
> Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
> Authored: Sun Mar 19 13:07:00 2017 -0300
> Committer: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
> Committed: Sun Mar 19 13:07:00 2017 -0300
>
> ----------------------------------------------------------------------
>  .../org/apache/tapestry5/TapestryConstants.java |  11 ++
>  .../tapestry5/annotations/PublishEvent.java     |  50 ++++++
>  .../corelib/mixins/PublishServerSideEvents.java | 151 +++++++++++++++++++
>  .../tapestry5/internal/InternalConstants.java   |  17 +++
>  .../internal/transform/OnEventWorker.java       |  54 ++++++-
>  .../org/apache/tapestry5/t5-core-dom.coffee     |  56 +++++++
>  .../src/test/app1/PublishEventDemo.tml          |   7 +
>  .../components/PublishEventDemoComponent.java   |  32 ++++
>  .../components/PublishEventDemoComponent2.java  |  32 ++++
>  .../tapestry5/integration/app1/pages/Index.java |  21 +--
>  .../app1/pages/PublishEventDemo.java            |  49 ++++++
>  .../META-INF/assets/PublishEventDemo.js         |  11 ++
>  .../components/PublishEventDemoComponent.tml    |   3 +
>  .../components/PublishEventDemoComponent2.tml   |   4 +
>  14 files changed, 482 insertions(+), 16 deletions(-)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> index ad1495a..cab8c46 100644
> --- a/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> +++ b/tapestry-core/src/main/java/org/apache/tapestry5/TapestryConstants.java
> @@ -12,6 +12,7 @@
>
>  package org.apache.tapestry5;
>
> +import org.apache.tapestry5.annotations.PublishEvent;
>  import org.apache.tapestry5.internal.structure.PageResetListener;
>  import org.apache.tapestry5.services.ComponentEventLinkEncoder;
>
> @@ -68,4 +69,14 @@ public class TapestryConstants
>       */
>      public static final String DISABLE_JAVASCRIPT_MINIMIZATION = "tapestry.disable-javascript-minimization";
>
> +    /**
> +     * Name of the HTML data attribute which contains information about component events
> +     * published by using the {@linkplain PublishEvent} annotation
> +     * in a component event handler method.
> +     *
> +     * @see PublishEvent
> +     * @since 5.4.2
> +     */
> +    public static final String COMPONENT_EVENTS_ATTRIBUTE_NAME = "data-component-events";
> +
>  }
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
> new file mode 100644
> index 0000000..18239d4
> --- /dev/null
> +++ b/tapestry-core/src/main/java/org/apache/tapestry5/annotations/PublishEvent.java
> @@ -0,0 +1,50 @@
> +// Licensed 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.tapestry5.annotations;
> +
> +import static java.lang.annotation.RetentionPolicy.RUNTIME;
> +import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.COMPONENT;
> +import static org.apache.tapestry5.ioc.annotations.AnnotationUseContext.PAGE;
> +
> +import java.lang.annotation.Documented;
> +import java.lang.annotation.ElementType;
> +import java.lang.annotation.Retention;
> +import java.lang.annotation.Target;
> +
> +import org.apache.tapestry5.ioc.annotations.UseWith;
> +
> +/**
> + * Marks an event handler method to be published as an event to be called in JavaScript
> + * through the <code>t5/core/triggerServerEvent</code> function.
> + *
> + * The event information is stored in JSON format inside the
> + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME} attribute.
> + *
> + * When used in a component method, the component must render at least one element,
> + * and that's what get the
> + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME} attribute above.
> + * If it doesn't, an exception will be thrown.
> + *
> + * When used in a page method, the page must render an &lt;body&gt; element,
> + * {@value org.apache.tapestry5.TapestryConstants#COMPONENT_EVENTS_ATTRIBUTE_NAME}. If it doesn't,
> + * an exception will be thrown.
> + *
> + * @since 5.4.2
> + */
> +@Target(ElementType.METHOD)
> +@Retention(RUNTIME)
> +@Documented
> +@UseWith({ COMPONENT, PAGE })
> +public @interface PublishEvent
> +{
> +}
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
> new file mode 100644
> index 0000000..3d73746
> --- /dev/null
> +++ b/tapestry-core/src/main/java/org/apache/tapestry5/corelib/mixins/PublishServerSideEvents.java
> @@ -0,0 +1,151 @@
> +// Licensed 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.tapestry5.corelib.mixins;
> +
> +import org.apache.tapestry5.ComponentResources;
> +import org.apache.tapestry5.MarkupWriter;
> +import org.apache.tapestry5.MarkupWriterListener;
> +import org.apache.tapestry5.TapestryConstants;
> +import org.apache.tapestry5.annotations.PublishEvent;
> +import org.apache.tapestry5.dom.Element;
> +import org.apache.tapestry5.internal.InternalConstants;
> +import org.apache.tapestry5.ioc.annotations.Inject;
> +import org.apache.tapestry5.json.JSONArray;
> +import org.apache.tapestry5.json.JSONObject;
> +import org.apache.tapestry5.model.ComponentModel;
> +
> +/**
> + * Tapestry internal mixin used to implement the {@link PublishEvent} event logic. Don't use directly.
> + */
> +public class PublishServerSideEvents
> +{
> +
> +    private static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY = InternalConstants.PUBLISH_COMPONENT_EVENTS_URL_PROPERTY;
> +    private static final String COMPONENT_EVENTS_ATTRIBUTE_NAME = TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME;
> +
> +    @Inject
> +    private ComponentResources resources;
> +
> +    void beginRender(final MarkupWriter writer) {
> +
> +        final Element element = writer.getElement();
> +
> +        // When the component is actually a page, nothing was rendered yet.
> +        // The listener we add here will add the events attribute to the <body> element
> +        // later
> +        if (element == null) {
> +            writer.addListener(new BodyElementListener(writer));
> +        }
> +        else {
> +            writer.addListener(new DelayedListener(writer));
> +        }
> +
> +    }
> +
> +    private void addEventsAttribute(final Element element)
> +    {
> +
> +        if (element == null)
> +        {
> +            throw new IllegalStateException("@PublishEvent used inside a page which didn't generate a <body> element");
> +        }
> +
> +        final ComponentResources containerResources = resources.getContainerResources();
> +        final ComponentModel componentModel = containerResources.getComponentModel();
> +        final String metaValue = componentModel.getMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META);
> +        final JSONArray componentEvents = new JSONArray(metaValue);
> +        final JSONObject events = new JSONObject();
> +        final String existingValue = element.getAttribute(COMPONENT_EVENTS_ATTRIBUTE_NAME);
> +
> +        if (existingValue != null)
> +        {
> +            final JSONObject existing = new JSONObject(existingValue);
> +            for (String key : existing.keys()) {
> +                events.put(key, existing.get(key));
> +            }
> +        }
> +
> +        for (int i = 0; i < componentEvents.length(); i++)
> +        {
> +            final String eventName = componentEvents.getString(i);
> +            JSONObject event = new JSONObject();
> +            event.put(PUBLISH_COMPONENT_EVENTS_URL_PROPERTY, containerResources.createEventLink(eventName).toString());
> +            events.put(eventName, event);
> +        }
> +
> +        element.forceAttributes(TapestryConstants.COMPONENT_EVENTS_ATTRIBUTE_NAME, events.toString());
> +    }
> +
> +    final private class DelayedListener implements MarkupWriterListener {
> +
> +        private MarkupWriter writer;
> +
> +        private Element element;
> +
> +        public DelayedListener(MarkupWriter writer)
> +        {
> +            super();
> +            this.writer = writer;
> +        }
> +
> +        @Override
> +        public void elementDidStart(Element element)
> +        {
> +            // Store first element generated by rendering the component
> +            if (this.element == null)
> +            {
> +                this.element = element;
> +            }
> +        }
> +
> +        @Override
> +        public void elementDidEnd(Element element)
> +        {
> +            if (this.element == null)
> +            {
> +                throw new IllegalStateException("@PublishEvent used inside a component which didn't generate any HTML elements");
> +            }
> +            addEventsAttribute(this.element);
> +            writer.removeListener(this);
> +        }
> +
> +    }
> +
> +    final private class BodyElementListener implements MarkupWriterListener {
> +
> +        private MarkupWriter writer;
> +
> +        public BodyElementListener(MarkupWriter writer)
> +        {
> +            super();
> +            this.writer = writer;
> +        }
> +
> +        @Override
> +        public void elementDidStart(Element element)
> +        {
> +            if (element.getName().equals("body"))
> +            {
> +                addEventsAttribute(element);
> +                writer.removeListener(this);
> +            }
> +        }
> +
> +        @Override
> +        public void elementDidEnd(Element element)
> +        {
> +        }
> +
> +    }
> +
> +}
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
> index 32b80a6..8370ae0 100644
> --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
> +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
> @@ -13,8 +13,10 @@
>  package org.apache.tapestry5.internal;
>
>  import org.apache.tapestry5.ContentType;
> +import org.apache.tapestry5.annotations.PublishEvent;
>  import org.apache.tapestry5.dom.MarkupModel;
>  import org.apache.tapestry5.ioc.util.TimeInterval;
> +import org.apache.tapestry5.model.ComponentModel;
>  import org.apache.tapestry5.services.javascript.JavaScriptStack;
>
>  public final class InternalConstants
> @@ -213,4 +215,19 @@ public final class InternalConstants
>       * @since 5.4
>       */
>      public static final ContentType JAVASCRIPT_CONTENT_TYPE = new ContentType("text/javascript");
> +
> +    /**
> +     * Name of the {@linkplain ComponentModel} metadata key whiche stores the {@linkplain PublishEvent}
> +     * data.
> +     * @since 5.4.2
> +     */
> +    public static final String PUBLISH_COMPONENT_EVENTS_META = "meta.publish-component-events";
> +
> +    /**
> +     * Name of the JSONObject key name which holds the name of the event to be published.
> +     *
> +     * @since 5.4.2
> +     */
> +    public static final String PUBLISH_COMPONENT_EVENTS_URL_PROPERTY = "url";
> +
>  }
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
> index fb4502b..fdb2dc9 100644
> --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
> +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/OnEventWorker.java
> @@ -12,15 +12,23 @@
>
>  package org.apache.tapestry5.internal.transform;
>
> +import java.lang.reflect.Array;
> +import java.util.Arrays;
> +import java.util.List;
> +import java.util.Map;
> +
>  import org.apache.tapestry5.ComponentResources;
>  import org.apache.tapestry5.EventContext;
>  import org.apache.tapestry5.ValueEncoder;
>  import org.apache.tapestry5.annotations.OnEvent;
> +import org.apache.tapestry5.annotations.PublishEvent;
>  import org.apache.tapestry5.annotations.RequestParameter;
> +import org.apache.tapestry5.corelib.mixins.PublishServerSideEvents;
>  import org.apache.tapestry5.func.F;
>  import org.apache.tapestry5.func.Flow;
>  import org.apache.tapestry5.func.Mapper;
>  import org.apache.tapestry5.func.Predicate;
> +import org.apache.tapestry5.internal.InternalConstants;
>  import org.apache.tapestry5.internal.services.ComponentClassCache;
>  import org.apache.tapestry5.ioc.OperationTracker;
>  import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> @@ -28,8 +36,19 @@ import org.apache.tapestry5.ioc.internal.util.InternalUtils;
>  import org.apache.tapestry5.ioc.internal.util.TapestryException;
>  import org.apache.tapestry5.ioc.util.ExceptionUtils;
>  import org.apache.tapestry5.ioc.util.UnknownValueException;
> +import org.apache.tapestry5.json.JSONArray;
>  import org.apache.tapestry5.model.MutableComponentModel;
> -import org.apache.tapestry5.plastic.*;
> +import org.apache.tapestry5.plastic.Condition;
> +import org.apache.tapestry5.plastic.InstructionBuilder;
> +import org.apache.tapestry5.plastic.InstructionBuilderCallback;
> +import org.apache.tapestry5.plastic.LocalVariable;
> +import org.apache.tapestry5.plastic.LocalVariableCallback;
> +import org.apache.tapestry5.plastic.MethodAdvice;
> +import org.apache.tapestry5.plastic.MethodDescription;
> +import org.apache.tapestry5.plastic.MethodInvocation;
> +import org.apache.tapestry5.plastic.PlasticClass;
> +import org.apache.tapestry5.plastic.PlasticField;
> +import org.apache.tapestry5.plastic.PlasticMethod;
>  import org.apache.tapestry5.runtime.ComponentEvent;
>  import org.apache.tapestry5.runtime.Event;
>  import org.apache.tapestry5.runtime.PageLifecycleListener;
> @@ -39,11 +58,6 @@ import org.apache.tapestry5.services.ValueEncoderSource;
>  import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
>  import org.apache.tapestry5.services.transform.TransformationSupport;
>
> -import java.lang.reflect.Array;
> -import java.util.Arrays;
> -import java.util.List;
> -import java.util.Map;
> -
>  /**
>   * Provides implementations of the
>   * {@link org.apache.tapestry5.runtime.Component#dispatchComponentEvent(org.apache.tapestry5.runtime.ComponentEvent)}
> @@ -151,6 +165,8 @@ public class OnEventWorker implements ComponentClassTransformWorker2
>          int minContextValues = 0;
>
>          boolean handleActivationEventContext = false;
> +
> +        final PublishEvent publishEvent;
>
>          EventHandlerMethod(PlasticMethod method)
>          {
> @@ -165,6 +181,8 @@ public class OnEventWorker implements ComponentClassTransformWorker2
>
>              eventType = extractEventType(methodName, onEvent);
>              componentId = extractComponentId(methodName, onEvent);
> +
> +            publishEvent = method.getAnnotation(PublishEvent.class);
>          }
>
>          void buildMatchAndInvocation(InstructionBuilder builder, final LocalVariable resultVariable)
> @@ -343,9 +361,31 @@ public class OnEventWorker implements ComponentClassTransformWorker2
>          implementDispatchMethod(plasticClass, isRoot, model, eventHandlerMethods);
>
>          addComponentIdValidationLogicOnPageLoad(plasticClass, eventHandlerMethods);
> +
> +        addPublishEventInfo(eventHandlerMethods, model);
> +    }
> +
> +    private void addPublishEventInfo(Flow<EventHandlerMethod> eventHandlerMethods,
> +            MutableComponentModel model)
> +    {
> +        JSONArray publishEvents = new JSONArray();
> +        for (EventHandlerMethod eventHandlerMethod : eventHandlerMethods)
> +        {
> +            if (eventHandlerMethod.publishEvent != null)
> +            {
> +                publishEvents.put(eventHandlerMethod.eventType.toLowerCase());
> +            }
> +        }
> +
> +        // If we do have events to publish, we apply the mixin and pass
> +        // event information to it.
> +        if (publishEvents.length() > 0) {
> +            model.addMixinClassName(PublishServerSideEvents.class.getName(), "after:*");
> +            model.setMeta(InternalConstants.PUBLISH_COMPONENT_EVENTS_META, publishEvents.toString());
> +        }
>      }
>
> -    private void addComponentIdValidationLogicOnPageLoad(PlasticClass plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
> +       private void addComponentIdValidationLogicOnPageLoad(PlasticClass plasticClass, Flow<EventHandlerMethod> eventHandlerMethods)
>      {
>          ComponentIdValidator[] validators = extractComponentIdValidators(eventHandlerMethods);
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
> index 9d419bc..9b975d4 100644
> --- a/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
> +++ b/tapestry-core/src/main/preprocessed-coffeescript/org/apache/tapestry5/t5-core-dom.coffee
> @@ -906,6 +906,60 @@ define ["underscore", "./utils", "./events", "jquery"],
>        events = utils.split events
>  #endif
>        onevent elements, events, match, handler
> +
> +    # Returns the URL of a component event based on its name and an optional element
> +    # or null if the event information is not found. When the element isn't passed
> +    # or it's null, the event data is taken from the <body> element.
> +    #
> +    # * eventName - (string) name of the component event
> +    # * element - (object) HTML DOM element to be used as the begining of the event data search. Optional.
> +    getEventUrl = (eventName, element) ->
> +
> +      if not (eventName?)
> +        throw 'dom.getEventUrl: the eventName parameter cannot be null'
> +
> +      if not _.isString eventName
> +        throw 'dom.getEventUrl: the eventName parameter should be a string'
> +
> +      eventName = eventName.toLowerCase()
> +
> +      if element is null
> +        element = document.getElementsByTagName('body')[0]
> +
> +      # Look for event data in itself first, then in the preceding siblings
> +      # if not found
> +      url = null
> +
> +      while not url? and element.previousElementSibling?
> +        data = getDataAttributeAsObject(element, 'component-events')
> +        url = data?[eventName]?.url
> +        element = element.previousElementSibling
> +
> +      if not url?
> +
> +        # Look at parent elements recursively
> +        while not url? and element.parentElement?
> +          data = getDataAttributeAsObject(element, 'component-events')
> +          url = data?[eventName]?.url
> +          element = element.parentElement;
> +
> +      return url;
> +
> +    # Returns the value of a given data attribute as an object.
> +    # The "data-" prefix is added automatically.
> +    # element - (object) HTML dom element
> +    # attribute - (string) name of the data attribute without the "data-" prefix.
> +    getDataAttributeAsObject = (element, attribute) ->
> +
> +#if jquery
> +      value = $(element).data(attribute)
> +#elseif prototype
> +      value = JSON.parse($(element).readAttribute('data-' + attribute))
> +      if value isnt null
> +        value = JSON.parse(value)
> +      else
> +        value = {}
> +#endif
>
>      # onDocument() is used to add an event handler to the document object; this is used
>      # for global (or default) handlers.
> @@ -919,5 +973,7 @@ define ["underscore", "./utils", "./events", "jquery"],
>      body: wrapElement document.body
>
>      scanner: scanner
> +
> +    getEventUrl : getEventUrl
>
>    return exports
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/app1/PublishEventDemo.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/app1/PublishEventDemo.tml b/tapestry-core/src/test/app1/PublishEventDemo.tml
> new file mode 100644
> index 0000000..43c2bd7
> --- /dev/null
> +++ b/tapestry-core/src/test/app1/PublishEventDemo.tml
> @@ -0,0 +1,7 @@
> +<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"
> +          xmlns:p="tapestry:parameter">
> +    <div id="page">
> +           <t:PublishEventDemoComponent/>
> +           <t:PublishEventDemoComponent2/>
> +    </div>
> +</t:border>
> \ No newline at end of file
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java
> new file mode 100644
> index 0000000..b8054f9
> --- /dev/null
> +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.java
> @@ -0,0 +1,32 @@
> +// Licensed 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.tapestry5.integration.app1.components;
> +
> +import org.apache.tapestry5.annotations.OnEvent;
> +import org.apache.tapestry5.annotations.PublishEvent;
> +import org.apache.tapestry5.json.JSONObject;
> +
> +public class PublishEventDemoComponent
> +{
> +    @OnEvent("answer")
> +    @PublishEvent
> +    JSONObject answer() {
> +        return new JSONObject("origin", "component");
> +    }
> +
> +    @PublishEvent
> +    JSONObject onAction()
> +    {
> +        return new JSONObject("origin", "component");
> +    }
> +
> +}
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java
> new file mode 100644
> index 0000000..7771557
> --- /dev/null
> +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.java
> @@ -0,0 +1,32 @@
> +// Licensed 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.tapestry5.integration.app1.components;
> +
> +import org.apache.tapestry5.annotations.OnEvent;
> +import org.apache.tapestry5.annotations.PublishEvent;
> +import org.apache.tapestry5.json.JSONObject;
> +
> +public class PublishEventDemoComponent2
> +{
> +    @PublishEvent
> +    JSONObject onAction()
> +    {
> +        return new JSONObject("origin", "component");
> +    }
> +
> +    @OnEvent("answer")
> +    @PublishEvent
> +    JSONObject answer() {
> +        return new JSONObject("origin", "component");
> +    }
> +
> +}
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> index d7d76d6..e78a874 100644
> --- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> @@ -57,32 +57,35 @@ public class Index
>
>      private static final List<Item> ITEMS = CollectionFactory
>              .newList(
> -                    new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."),
> +
> +                  new Item("PublishEventDemo", "@PublishEvent Demo", "Publishing server-side events to client-side code (JavaScript)"),
> +
> +//                    new Item("ZoneFormDemo", "Zone Form Decoration", "Fields inside an Ajax-updatd Form are still decorated properly."),
>
> -                    new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."),
> +//                    new Item("AjaxValidationDemo", "Ajax Validation", "Demonstrated proper integration of server-side validation and client-side field decoration."),
>
>                      new Item("OverrideEventHandlerDemo", "Event Handler Override Demo", "Event Handler methods overridden by sub-classes invoke base-class correctly."),
>
> -                    new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."),
> +//                    new Item("LogoSubclass", "Base class Assets in sub-classes", "Assets are resolved for the parent class if that's where the annotations are."),
>
>                      new Item("MissingRequiredARP", "Missing Query Parameter for @ActivationRequestParameter", "Activating a page with a required @ActivationRequestParameter, but no matching query parameter, is an error."),
>
> -                    new Item("DateFieldValidationDemo", "DateField Validation Demo",
> -                            "Use of DateField component when client validation is disabled."),
> +//                    new Item("DateFieldValidationDemo", "DateField Validation Demo",
> +//                            "Use of DateField component when client validation is disabled."),
>
>                      new Item("MixinParameters54", "Strict Mixin Parameters", "In the 5.4 DTD, Parameter Mixins must be qualified with the mixin id."),
>
> -                    new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."),
> +//                    new Item("AsyncDemo", "Async Links and Forms Demo", "Async (XHR) Updates without a containing Zone."),
>
>                      new Item("FormCancelActionDemo", "Form Cancel Action Demo", "FormSupport.addCancel() support"),
>
>                      new Item("AjaxRadioDemo", "Ajax Radio Demo", "Radio components inside an Ajax form"),
>
> -                    new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"),
> +//                    new Item("TimeIntervalDemo", "TimeInterval Demo", "Interval component, based on Moment.js"),
>
> -                    new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"),
> +//                    new Item("LocalDateDemo", "LocalDate Demo", "LocalDate component, based on Moment.js"),
>
> -                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."),
> +//                    new Item("EmptyIfDemo", "Empty If Demo", "Ensure an empty If can still render."),
>
>                      new Item("MissingAssetDemo", "Missing Asset Demo", "Error when injecting an asset that does not exist."),
>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
> new file mode 100644
> index 0000000..1876de5
> --- /dev/null
> +++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/PublishEventDemo.java
> @@ -0,0 +1,49 @@
> +// Copyright 2016 The Apache Software Foundation
> +//
> +// Licensed 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.
> +
> +// Licensed 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.tapestry5.integration.app1.pages;
> +
> +import org.apache.tapestry5.annotations.Import;
> +import org.apache.tapestry5.annotations.OnEvent;
> +import org.apache.tapestry5.annotations.PublishEvent;
> +import org.apache.tapestry5.json.JSONObject;
> +
> +@Import(stack = "core", library = "PublishEventDemo.js")
> +public class PublishEventDemo
> +{
> +
> +    @PublishEvent
> +    JSONObject onAction()
> +    {
> +        return new JSONObject("origin", "page");
> +    }
> +
> +    @OnEvent("answer")
> +    @PublishEvent
> +    JSONObject answer() {
> +        return new JSONObject("origin", "page");
> +    }
> +
> +}
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> new file mode 100644
> index 0000000..20c0406
> --- /dev/null
> +++ b/tapestry-core/src/test/resources/META-INF/assets/PublishEventDemo.js
> @@ -0,0 +1,11 @@
> +require(["t5/core/dom", "t5/core/ajax", "jquery"], function (dom, ajax, $) {
> +
> +    $(document).ready(function() {
> +        console.log('dom.getEventURL()   : ' + dom.getEventUrl('answer', document.getElementById("page")));
> +        console.log('dom.getEventURL() 1 : ' + dom.getEventUrl('answer', document.getElementById("componentParagraph")));
> +        console.log('dom.getEventURL() 2 : ' + dom.getEventUrl('answer', document.getElementById("componentParagraph2")));
> +        console.log('dom.getEventURL() 3 : ' + dom.getEventUrl('answer', document.getElementById("componentParagraph3")));
> +    });
> +
> +});
> +
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> new file mode 100644
> index 0000000..2d2b5f1
> --- /dev/null
> +++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent.tml
> @@ -0,0 +1,3 @@
> +<div id="component" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
> +       <p id="componentParagraph">I'm a component</p>
> +</div>
>
> http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6b4ca30b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> ----------------------------------------------------------------------
> diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> new file mode 100644
> index 0000000..b10d020
> --- /dev/null
> +++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/PublishEventDemoComponent2.tml
> @@ -0,0 +1,4 @@
> +<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
> +       <p id="componentParagraph2">I'm another component</p>
> +       <p id="componentParagraph3">I'm another component</p>
> +</t:container>
>



-- 
Massimo Lusetti


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org