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: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.tags.html;
020:
021: import org.apache.beehive.netui.tags.ByRef;
022: import org.apache.beehive.netui.tags.IHtmlAccessable;
023: import org.apache.beehive.netui.tags.internal.PageFlowTagUtils;
024: import org.apache.beehive.netui.tags.javascript.CoreScriptFeature;
025: import org.apache.beehive.netui.tags.javascript.ScriptRequestState;
026: import org.apache.beehive.netui.tags.rendering.AbstractHtmlState;
027: import org.apache.beehive.netui.tags.rendering.ImageTag;
028: import org.apache.beehive.netui.tags.rendering.TagRenderingBase;
029: import org.apache.beehive.netui.tags.rendering.WriteRenderAppender;
030: import org.apache.beehive.netui.util.Bundle;
031:
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpServletResponse;
034: import javax.servlet.jsp.JspException;
035: import java.net.URISyntaxException;
036:
037: /**
038: * Generates a URL-encoded hyperlink to a specified URI with an image
039: * enclosed as the body. ImageAnchor provides support for image rollovers.
040: *
041: * An imageAnchor must have one of five attributes to correctly create the hyperlink:
042: * <ul>
043: * <li>action - an action invoked by clicking the hyperlink.</li>
044: * <li>href - a URL to go to</li>
045: * <li>linkName - an internal place in the page to move to</li>
046: * <li>clientAction - the action to run on the client</li>
047: * <li>tagId - the ID of the tag</li>
048: * <li>formSubmit - indicates whether or not the enclosing Form should be submitted</li>
049: * </ul>
050: * @jsptagref.tagdescription <p>
051: * Generates a hyperlink with a clickable image. Provides support for image rollovers.
052: * <p>
053: * The <netui:imageAnchor> tag must have one of five attributes to correctly create the hyperlink:
054: * <blockquote>
055: * <ul>
056: * <li><code>action</code> - an action method invoked by clicking the hyperlink</li>
057: * <li><code>href</code> - an URL to go to</li>
058: * <li><code>linkName</code> - an internal place in the page to move to</li>
059: * <li><code>clientAction</code> - the action to run on the client</li>
060: * <li><code>tagId</code> - the ID of the tag</li>
061: * <li><code>formSubmit</code> - indicates whether or not the enclosing Form should be submitted</li>
062: * </ul>
063: * </blockquote>
064: *
065: * <p><b>JavaScript</b></p>
066: * <p>If the <code>formSubmit</code> attribute is set to <code>true</code> and no
067: * <code>onClick</code> attribute is set, the following JavaScript function will be written to the HTML page.
068: * This JavaScript function will be referenced by the <code>onclick</code> attribute of the generated image
069: * anchor tag.</p>
070: * <pre>
071: * function anchor_submit_form(netuiName, newAction)
072: * {
073: * for (var i=0; i<document.forms.length; i++) {
074: * if (document.forms[i].id == netuiName) {
075: * document.forms[i].method = "POST";
076: * document.forms[i].action = newAction;
077: * document.forms[i].submit();
078: * }
079: * }
080: * }</pre>
081: * <p>It is possible to write a custom <code>onClick</code> JavaScript event handler that would
082: * do additional work, for example form validation, and still submit the form correctly. To
083: * accomplish this, reference a JavaScript function in the <code>onClick</code>
084: * attribute:</p>
085: * <pre> <netui:imageAnchor formSubmit="true" <b>onClick="SubmitFromAnchor();return false;"</b>>
086: * View Results
087: * </netui:imageAnchor></pre>
088: *
089: * <p>And add the referenced JavaScript function to the page:</p>
090: * <pre> function SubmitFromAnchor()
091: * {
092: * // implement custom logic here
093: *
094: * for(var i=0; i<document.forms.length; i++)
095: * {
096: * // submit to the action /aWebapp/formPost.do
097: * if (document.forms[i].action == "/aWebapp/formPost.do")
098: * {
099: * document.forms[i].method="POST";
100: * document.forms[i].action="/aWebapp/formPost.do";
101: * document.forms[i].submit();
102: * }
103: * }
104: * }</pre>
105: *
106: * <p>This will cause the JavaScript function to be executed before posting the form.</p>
107: * @example In this sample, an <netui:imageAnchor> shows "top.jpg" at 25 x 25 pixels and navigates to
108: * index.jsp when clicked.
109: * <pre> <netui:imageAnchor href="index.jsp" src="top.jpg" height="25" width="25" /></pre>
110: * </p>
111: * @netui:tag name="imageAnchor" description="Combines the functionality of the netui:image and netui:anchor tags."
112: */
113: public class ImageAnchor extends Anchor implements IHtmlAccessable {
114: private ImageTag.State _imgState = new ImageTag.State();
115: private String _rolloverImage = null; // The roll-over image of the ImageAnchor.
116:
117: /**
118: * Return the name of the Tag.
119: */
120: public String getTagName() {
121: return "ImageAnchor";
122: }
123:
124: /**
125: * Base support for the attribute tag. This is overridden to prevent setting the <code>src</code>
126: * attribute. ImageAnchor supports two facets, <code>image</code> and <code>anchor<code>. The default
127: * facet is anchor, meaning if the facet is not specified, the attribute will be applied to the
128: * <a> element. To apply an attribute to the <img> element you must specify the
129: * <code>image</code> facet.
130: * @param name The name of the attribute. This value may not be null or the empty string.
131: * @param value The value of the attribute. This may contain an expression.
132: * @param facet The name of a facet to which the attribute will be applied. This is optional.
133: * @throws JspException A JspException may be thrown if there is an error setting the attribute.
134: */
135: public void setAttribute(String name, String value, String facet)
136: throws JspException {
137: if (name != null && name.equals(SRC)) {
138: String s = Bundle.getString("Tags_AttributeMayNotBeSet",
139: new Object[] { name });
140: registerTagError(s, null);
141: }
142: if (facet != null) {
143: if (facet.equals("image")) {
144: setStateAttribute(name, value, _imgState);
145: return;
146: }
147: }
148: super .setAttribute(name, value, facet);
149: }
150:
151: /**
152: * Sets the property to specify where to align the image.
153: * @param align the image alignment.
154: * @jsptagref.attributedescription The alignment of the image.
155: * @jsptagref.databindable false
156: * @jsptagref.attributesyntaxvalue <i>string_align</i>
157: * @netui:attribute required="false" rtexprvalue="true"
158: * description="The alignment of the image."
159: */
160: public void setAlign(String align) {
161: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
162: ALIGN, align);
163: }
164:
165: /**
166: * Sets the property to specify the alt text of the image.
167: * @param alt the image alignment.
168: * @jsptagref.attributedescription The alternative text of the image
169: * @jsptagref.databindable Read Only
170: * @jsptagref.attributesyntaxvalue <i>string_alt</i>
171: * @netui:attribute required="false" rtexprvalue="true"
172: * description="The alternative text of the image."
173: */
174: public void setAlt(String alt) {
175: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
176: ALT, alt);
177: }
178:
179: /**
180: * Sets the property to specify a link to the the long description to supplement
181: * the short description in the <code>alt</code> attribute.
182: * @param longdesc the longdesc.
183: * @jsptagref.attributedescription Specifies a link to the the long description.
184: * @jsptagref.databindable false
185: * @jsptagref.attributesyntaxvalue <i>string_longdesc</i>
186: * @netui:attribute required="false" rtexprvalue="true"
187: * description="Specifies a link to the the long description."
188: */
189: public void setLongdesc(String longdesc) {
190: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
191: LONGDESC, longdesc);
192: }
193:
194: /**
195: * Sets the border size around the image.
196: * @param border the border size.
197: * @jsptagref.attributedescription The border size around the image
198: * @jsptagref.databindable false
199: * @jsptagref.attributesyntaxvalue <i>integer_pixelBorder</i>
200: * @netui:attribute required="false" rtexprvalue="true"
201: * description="The border size around the image."
202: */
203: public void setBorder(String border) {
204: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
205: BORDER, border);
206: }
207:
208: /**
209: * Sets the image height.
210: * @param height the height.
211: * @jsptagref.attributedescription The image height
212: * @jsptagref.databindable Read Only
213: * @jsptagref.attributesyntaxvalue <i>integer_height</i>
214: * @netui:attribute required="false" rtexprvalue="true"
215: * description="The image height."
216: */
217: public void setHeight(String height) {
218: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
219: HEIGHT, height);
220: }
221:
222: /**
223: * Sets the the horizontal spacing around the image.
224: * @param hspace the horizontal spacing.
225: * @jsptagref.attributedescription The horizontal spacing around the image.
226: * @jsptagref.databindable Read Only
227: * @jsptagref.attributesyntaxvalue <i>integer_hspace</i>
228: * @netui:attribute required="false" rtexprvalue="true"
229: * description="The horizontal spacing around the image."
230: */
231: public void setHspace(String hspace) {
232: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
233: HSPACE, hspace);
234: }
235:
236: /**
237: * Sets the server-side image map declaration.
238: * @param ismap the image map declaration.
239: * @jsptagref.attributedescription The server-side map declaration.
240: * @jsptagref.databindable false
241: * @jsptagref.attributesyntaxvalue <i>string_isMap</i>
242: * @netui:attribute required="false" rtexprvalue="true"
243: * description="The server-side map declaration."
244: */
245: public void setIsmap(String ismap) {
246: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
247: ISMAP, ismap);
248: }
249:
250: /**
251: * Sets the roll-over image of the ImageAnchor.
252: * @param rolloverImage the rollover image.
253: * @jsptagref.attributedescription The URI of the rollover image.
254: * @jsptagref.databindable false
255: * @jsptagref.attributesyntaxvalue <i>string_rolloverImage</i>
256: * @netui:attribute required="false" rtexprvalue="true"
257: * description="The URI of the rollover image."
258: */
259: public void setRolloverImage(String rolloverImage) {
260: _rolloverImage = rolloverImage;
261: }
262:
263: /**
264: * Sets the image source URI.
265: * @param src the image source URI.
266: * @jsptagref.attributedescription The image source URI
267: * @jsptagref.databindable Read Only
268: * @jsptagref.attributesyntaxvalue <i>string_src</i>
269: * @netui:attribute required="false" rtexprvalue="true"
270: * description="The image source URI"
271: * reftype="img-url"
272: */
273: public void setSrc(String src) throws JspException {
274: _imgState.src = src;
275: }
276:
277: /**
278: * Sets the client-side image map declaration.
279: * @param usemap the map declaration.
280: * @jsptagref.attributedescription The client-side image map declaration
281: * @jsptagref.databindable false
282: * @jsptagref.attributesyntaxvalue <i>string_useMap</i>
283: * @netui:attribute required="false" rtexprvalue="true"
284: * description="The client-side image map declaration"
285: */
286: public void setUsemap(String usemap) {
287: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
288: USEMAP, usemap);
289: }
290:
291: /**
292: * Sets the vertical spacing around the image.
293: * @param vspace the vertical spacing.
294: * @jsptagref.attributedescription The vertical spacing around the image.
295: * @jsptagref.databindable Read Only
296: * @jsptagref.attributesyntaxvalue <i>string_vspace</i>
297: * @netui:attribute required="false" rtexprvalue="true"
298: * description="The vertical spacing around the image."
299: */
300: public void setVspace(String vspace) {
301: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
302: VSPACE, vspace);
303: }
304:
305: /**
306: * Set the <img> style for the contained image. When the tag library is
307: * running in legacy mode, this will override the <code>style</code> attribute if that is
308: * set. If this is not set, and <code>style</code> is set, then it will be applied to
309: * the image.
310: * @param imageStyle the label style
311: * @jsptagref.attributedescription For legacy documents. Specifies style information for the
312: * contained image. When the tag library is running in legacy mode, this will override the
313: * <code>style</code> attribute. If this is not set, and <code>style</code> is set,
314: * then it will be applied to the image.
315: * @jsptagref.databindable false
316: * @jsptagref.attributesyntaxvalue <i>string_imagestyle</i>
317: * @netui:attribute required="false" rtexprvalue="true"
318: * description="Set the style for the contained image."
319: */
320: public void setImageStyle(String imageStyle) {
321: _imgState.style = imageStyle;
322: }
323:
324: /**
325: * Set the label style class for each contained Image. When the tag library is
326: * running in legacy mode, this will override the <code>styleClass</code> attribute if that is
327: * set. If this is not set, and <code>styleClass</code> is set, then it will be applied to
328: * the image.
329: * @param imageClass the image class
330: * @jsptagref.attributedescription For legacy documents. The style class (a style sheet selector).
331: * When the tag library is running in legacy mode, this will override the <code>styleClass</code>
332: * attribute. If this is not set, and <code>styleClass</code> is set, then it will be applied to
333: * the image.
334: * @jsptagref.databindable false
335: * @jsptagref.attributesyntaxvalue <i>string_imageclass</i>
336: * @netui:attribute required="false" rtexprvalue="true"
337: * description="Set the label style class for each contained image."
338: */
339: public void setImageStyleClass(String imageClass) {
340: _imgState.styleClass = imageClass;
341: }
342:
343: /**
344: * Sets the image width.
345: * @param width the image width.
346: * @jsptagref.attributedescription The image width.
347: * @jsptagref.databindable Read Only
348: * @jsptagref.attributesyntaxvalue <i>integer_pixelWidth</i>
349: * @netui:attribute required="false" rtexprvalue="true"
350: * description="The image width."
351: */
352: public void setWidth(String width) {
353: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
354: WIDTH, width);
355: }
356:
357: /**
358: * Insert rollover javascript.
359: * <p>
360: * Support for indexed property since Struts 1.1
361: * @throws JspException if a JSP exception has occurred
362: */
363: public int doStartTag() throws JspException {
364: if (_rolloverImage != null
365: && getJavaScriptAttribute(ONMOUSEOVER) == null) {
366: // cause the roll over script to be inserted
367: WriteRenderAppender writer = new WriteRenderAppender(
368: pageContext);
369: ScriptRequestState srs = ScriptRequestState
370: .getScriptRequestState((HttpServletRequest) pageContext
371: .getRequest());
372: srs.writeFeature(getScriptReporter(), writer,
373: CoreScriptFeature.ROLLOVER, true, false, null);
374: }
375:
376: return EVAL_BODY_BUFFERED;
377: }
378:
379: /**
380: * Render the image and hyperlink.
381: * @throws JspException if a JSP exception has occurred
382: */
383: public int doEndTag() throws JspException {
384: // report errors that may have occurred when the required attributes are being set
385: if (hasErrors())
386: return reportAndExit(EVAL_PAGE);
387:
388: HttpServletRequest req = (HttpServletRequest) pageContext
389: .getRequest();
390:
391: // build the anchor into the results
392: // render the anchor tag
393: WriteRenderAppender writer = new WriteRenderAppender(
394: pageContext);
395: TagRenderingBase trb = TagRenderingBase.Factory.getRendering(
396: TagRenderingBase.ANCHOR_TAG, req);
397: ByRef script = new ByRef();
398: if (!createAnchorBeginTag(req, script, trb, writer,
399: REQUIRED_ATTR)) {
400: reportErrors();
401: if (!script.isNull())
402: write(script.getRef().toString());
403: localRelease();
404: return EVAL_PAGE;
405: }
406:
407: // set the source and lowsrc attributes
408: // the lowsrc is deprecated and should be removed.
409: HttpServletResponse response = (HttpServletResponse) pageContext
410: .getResponse();
411: if (_imgState.src != null) {
412: try {
413: String uri = PageFlowTagUtils.rewriteResourceURL(
414: pageContext, _imgState.src, null, null);
415: _imgState.src = response.encodeURL(uri);
416: } catch (URISyntaxException e) {
417: // report the error...
418: String s = Bundle.getString("Tags_Image_URLException",
419: new Object[] { _imgState.src, e.getMessage() });
420: registerTagError(s, e);
421: }
422: }
423:
424: // set the rollover image
425: if (_rolloverImage != null) {
426: try {
427: String uri = PageFlowTagUtils.rewriteResourceURL(
428: pageContext, _rolloverImage, null, null);
429: _rolloverImage = response.encodeURL(uri);
430: } catch (URISyntaxException e) {
431: // report the error...
432: String s = Bundle
433: .getString("Tags_Rollover_Image_URLException",
434: new Object[] { _rolloverImage,
435: e.getMessage() });
436: registerTagError(s, e);
437: }
438:
439: if (getJavaScriptAttribute(ONMOUSEOUT) == null) {
440: String s = "swapImage(this,'"
441: + response.encodeURL(_imgState.src) + "')";
442: _imgState.registerAttribute(
443: AbstractHtmlState.ATTR_JAVASCRIPT, ONMOUSEOUT,
444: s);
445: }
446: if (getJavaScriptAttribute(ONMOUSEOVER) == null) {
447: String s = "swapImage(this,'"
448: + response.encodeURL(_rolloverImage) + "')";
449: _imgState.registerAttribute(
450: AbstractHtmlState.ATTR_JAVASCRIPT, ONMOUSEOVER,
451: s);
452: }
453: }
454:
455: // render the image tag.
456: TagRenderingBase br = TagRenderingBase.Factory.getRendering(
457: TagRenderingBase.IMAGE_TAG, req);
458: br.doStartTag(writer, _imgState);
459: br.doEndTag(writer);
460:
461: // write the end tag
462: trb.doEndTag(writer);
463: if (!script.isNull())
464: write(script.getRef().toString());
465:
466: localRelease();
467: return EVAL_PAGE;
468: }
469:
470: /**
471: * Release any acquired resources.
472: */
473: protected void localRelease() {
474: super.localRelease();
475:
476: _imgState.clear();
477: _rolloverImage = null;
478: }
479: }
|