001// Copyright 2004, 2005 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.tapestry.html;
016
017import java.util.HashMap;
018import java.util.Map;
019
020import org.apache.hivemind.ApplicationRuntimeException;
021import org.apache.tapestry.AbstractComponent;
022import org.apache.tapestry.IAsset;
023import org.apache.tapestry.IMarkupWriter;
024import org.apache.tapestry.IRequestCycle;
025import org.apache.tapestry.IScript;
026import org.apache.tapestry.PageRenderSupport;
027import org.apache.tapestry.Tapestry;
028import org.apache.tapestry.TapestryUtils;
029import org.apache.tapestry.components.ILinkComponent;
030import org.apache.tapestry.components.LinkEventType;
031
032/**
033 * Combines a link component (such as {@link org.apache.tapestry.link.DirectLink}) with an
034 * <img> and JavaScript code to create a rollover effect that works with both Netscape
035 * Navigator and Internet Explorer. [ <a
036 * href="../../../../../ComponentReference/Rollover.html">Component Reference </a>]
037 * 
038 * @author Howard Lewis Ship
039 */
040
041public abstract class Rollover extends AbstractComponent
042{
043    /**
044     * Converts an {@link IAsset}binding into a usable URL. Returns null if the binding does not
045     * exist or the binding's value is null.
046     */
047
048    protected String getAssetURL(IAsset asset)
049    {
050        if (asset == null)
051            return null;
052
053        return asset.buildURL();
054    }
055
056    protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
057    {
058        // No body, so we skip it all if not rewinding (assumes no side effects on
059        // accessors).
060
061        if (cycle.isRewinding())
062            return;
063
064        String imageURL = null;
065        String mouseOverURL = null;
066        String mouseOutURL = null;
067        boolean dynamic = false;
068        String imageId = null;
069
070        PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
071
072        ILinkComponent serviceLink = (ILinkComponent) cycle
073                .getAttribute(Tapestry.LINK_COMPONENT_ATTRIBUTE_NAME);
074
075        if (serviceLink == null)
076            throw new ApplicationRuntimeException(Tapestry
077                    .getMessage("Rollover.must-be-contained-by-link"), this, null, null);
078
079        boolean linkDisabled = serviceLink.isDisabled();
080
081        if (linkDisabled)
082        {
083            imageURL = getAssetURL(getDisabled());
084
085            if (imageURL == null)
086                imageURL = getAssetURL(getImage());
087        }
088        else
089        {
090            imageURL = getAssetURL(getImage());
091            mouseOverURL = getAssetURL(getMouseOver());
092            mouseOutURL = getAssetURL(getMouseOut());
093
094            dynamic = (mouseOverURL != null) || (mouseOutURL != null);
095        }
096
097        if (imageURL == null)
098            throw Tapestry.createRequiredParameterException(this, "image");
099
100        writer.beginEmpty("img");
101
102        writer.attribute("src", imageURL);
103
104        if (dynamic)
105        {
106            if (mouseOverURL == null)
107                mouseOverURL = imageURL;
108
109            if (mouseOutURL == null)
110                mouseOutURL = imageURL;
111
112            imageId = writeScript(cycle, pageRenderSupport, serviceLink, mouseOverURL, mouseOutURL);
113
114            writer.attribute("id", imageId);
115        }
116
117        renderInformalParameters(writer, cycle);
118
119        writer.closeTag();
120
121    }
122
123    // Injected
124
125    public abstract IScript getScript();
126
127    private String writeScript(IRequestCycle cycle, PageRenderSupport pageRenderSupport,
128            ILinkComponent link, String mouseOverImageURL, String mouseOutImageURL)
129    {
130        String imageId = pageRenderSupport.getUniqueString(getId());
131        String preloadedMouseOverImageURL = pageRenderSupport
132                .getPreloadedImageReference(mouseOverImageURL);
133        String preloadedMouseOutImageURL = pageRenderSupport
134                .getPreloadedImageReference(mouseOutImageURL);
135
136        Map symbols = new HashMap();
137
138        symbols.put("imageId", imageId);
139        symbols.put("mouseOverImageURL", preloadedMouseOverImageURL);
140        symbols.put("mouseOutImageURL", preloadedMouseOutImageURL);
141
142        getScript().execute(cycle, pageRenderSupport, symbols);
143
144        // Add attributes to the link to control mouse over/out.
145        // Because the script is written before the <body> tag,
146        // there won't be any timing issues (such as cause
147        // bug #113893).
148
149        link.addEventHandler(LinkEventType.MOUSE_OVER, (String) symbols.get("onMouseOverName"));
150        link.addEventHandler(LinkEventType.MOUSE_OUT, (String) symbols.get("onMouseOutName"));
151
152        return imageId;
153    }
154
155    public abstract IAsset getMouseOut();
156
157    public abstract IAsset getDisabled();
158
159    public abstract IAsset getMouseOver();
160
161    public abstract IAsset getImage();
162}