001// Copyright 2011, 2012 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.corelib.mixins;
016
017import org.apache.tapestry5.BindingConstants;
018import org.apache.tapestry5.ComponentResources;
019import org.apache.tapestry5.EventConstants;
020import org.apache.tapestry5.EventContext;
021import org.apache.tapestry5.Link;
022import org.apache.tapestry5.annotations.AfterRender;
023import org.apache.tapestry5.annotations.Events;
024import org.apache.tapestry5.annotations.InjectContainer;
025import org.apache.tapestry5.annotations.Parameter;
026import org.apache.tapestry5.corelib.components.Zone;
027import org.apache.tapestry5.internal.util.CaptureResultCallback;
028import org.apache.tapestry5.ioc.annotations.Inject;
029import org.apache.tapestry5.services.javascript.JavaScriptSupport;
030
031/**
032 * <p>
033 * This mixin periodically refreshs a {@link org.apache.tapestry5.corelib.components.Zone zone}
034 * by triggering an event on the server using ajax requests.
035 * </p>
036 * <p>
037 * Server-side, the mixin triggers the "refresh" event with the mixin's context. A container may but
038 * does not need to handle the event. If the event is handled and a value is returned, that value is
039 * used to render the response. Otherwise, the Zone's body is re-rendered.
040 * </p>
041 * <b>Note: </b> This mixin is only meant for a {@link org.apache.tapestry5.corelib.components.Zone zone}.
042 *
043 * @tapestrydoc
044 */
045@Events(EventConstants.REFRESH)
046public class ZoneRefresh
047{
048    /**
049     * Period between two consecutive refreshes (in seconds). If a new refresh occurs before the
050     * previous refresh has completed, it will be skipped.
051     */
052    @Parameter(required = true, defaultPrefix = BindingConstants.LITERAL)
053    private int period;
054
055    /**
056     * Context passed to the event
057     */
058    @Parameter
059    private Object[] context;
060
061    @InjectContainer
062    private Zone zone;
063
064    @Inject
065    private JavaScriptSupport javaScriptSupport;
066
067    @Inject
068    private ComponentResources resources;
069
070    //For testing purpose
071    ZoneRefresh(Object[] context, ComponentResources resources, JavaScriptSupport javaScriptSupport, Zone zone)
072    {
073        this.context = context;
074        this.resources = resources;
075        this.javaScriptSupport = javaScriptSupport;
076        this.zone = zone;
077    }
078
079    @AfterRender
080    void addJavaScript()
081    {
082        Link link = resources.createEventLink("zoneRefresh", context);
083
084        javaScriptSupport.require("t5/core/zone-refresh").with(zone.getClientId(), period, link.toString());
085    }
086
087    Object onZoneRefresh(EventContext eventContext)
088    {
089        CaptureResultCallback<Object> callback = new CaptureResultCallback<Object>();
090        resources.triggerContextEvent(EventConstants.REFRESH, eventContext, callback);
091
092        if (callback.getResult() != null)
093        {
094            return callback.getResult();
095        }
096
097        return zone.getBody();
098    }
099
100}