001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.extensions.ajax.markup.html;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
021import org.apache.wicket.ajax.IAjaxIndicatorAware;
022import org.apache.wicket.behavior.Behavior;
023import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
024import org.apache.wicket.markup.head.IHeaderResponse;
025import org.apache.wicket.request.IRequestHandler;
026import org.apache.wicket.request.Response;
027import org.apache.wicket.request.cycle.RequestCycle;
028import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
029import org.apache.wicket.request.resource.ResourceReference;
030
031/**
032 * A behavior that adds a span with wicket's default indicator gif to the end of the component's
033 * markup. This span can be used as an ajax busy indicator. For an example usage see
034 * {@link IndicatingAjaxLink}
035 * <p>
036 * Instances of this behavior must not be shared between components.
037 * 
038 * @see IndicatingAjaxLink
039 * @see IAjaxIndicatorAware
040 * 
041 * @since 1.2
042 * 
043 * @author Igor Vaynberg (ivaynberg)
044 */
045public class AjaxIndicatorAppender extends Behavior
046{
047        private static final long serialVersionUID = 1L;
048
049        /**
050         * Component instance this behavior is bound to
051         */
052        private Component component;
053
054        private final ResourceReference indicator;
055
056        /**
057         * Construct.
058         */
059        public AjaxIndicatorAppender()
060        {
061                this(AbstractDefaultAjaxBehavior.INDICATOR);
062        }
063        
064        /**
065         * Build a new instance specifying a custom busy indicator as {@link ResourceReference}.
066         * 
067         * @param indicator
068         *             The {@link ResourceReference} to use as busy indicator.     
069         */
070        public AjaxIndicatorAppender(ResourceReference indicator)
071        {
072                this.indicator = indicator;
073        }
074
075        @Override
076        public void renderHead(final Component component, final IHeaderResponse response)
077        {
078                super.renderHead(component, response);
079
080                component.getRequestCycle().find(IPartialPageRequestHandler.class).ifPresent(target -> {
081                        final String javascript = "var e = Wicket.$('" + getMarkupId() +
082                                "'); if (e != null && typeof(e.parentNode) != 'undefined') e.parentNode.removeChild(e);";
083
084                        target.prependJavaScript(javascript);
085                });
086        }
087
088
089        @Override
090        public void afterRender(final Component component)
091        {
092                super.afterRender(component);
093                final Response r = component.getResponse();
094
095                r.write("<span hidden=\"\" class=\"");
096                r.write(getSpanClass());
097                r.write("\" ");
098                r.write("id=\"");
099                r.write(getMarkupId());
100                r.write("\">");
101                r.write("<img src=\"");
102                r.write(getIndicatorUrl());
103                r.write("\" alt=\"\"/></span>");
104
105        }
106
107        /**
108         * @return url of the animated indicator image
109         */
110        protected CharSequence getIndicatorUrl()
111        {
112                IRequestHandler handler = new ResourceReferenceRequestHandler(indicator);
113                return RequestCycle.get().urlFor(handler);
114        }
115
116        /**
117         * @return css class name of the generated outer span
118         */
119        protected String getSpanClass()
120        {
121                return "wicket-ajax-indicator";
122        }
123
124        /**
125         * Returns the markup id attribute of the outer most span of this indicator. This is the id of
126         * the span that should be hidden or show to hide or show the indicator.
127         * 
128         * @return markup id of outer most span
129         */
130        public String getMarkupId()
131        {
132                return component.getMarkupId() + "--ajax-indicator";
133        }
134
135        @Override
136        public final void bind(final Component component)
137        {
138                this.component = component;
139        }
140}