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