package er.ajax; import com.webobjects.appserver.WOActionResults; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WORequest; import com.webobjects.appserver.WOResponse; import er.extensions.appserver.ERXWOContext; import er.extensions.components.conditionals.ERXWOTemplate; import er.extensions.foundation.ERXStringUtilities; /** * <p> * {@code AjaxExpansion} provides an easy way to make expansion areas that * appear and disappear by clicking a link. (For instance, expandable options * areas). The simple implementation of an expansion area would include wrapping * the toggle link in the {@link AjaxUpdateContainer}. The problem with this * approach is that if you want to animate the appearance of the contents, the * animation affects the link as well as the contents. {@code AjaxExpansion} * instead only updates the contents, and applies an "expanded" class to the * link, which you can use to change the expansion icon in a stylesheet. (See * AjaxExample2's ToggleDetails example). * </p> * * <p> * You can use an {@code openedLabel} and {@code closedLabel} binding to change * the link text. Or, if you want to use something fancier than a string as link * label, you can put an {@link ERXWOTemplate} with {@code templateName='label'} * inside the component. If present, that will replace the label provided by the * {@code string} binding. * </p> * * <h3>Example</h3> * * <pre> * <code><wo:AjaxExpansion id="contact" class="contact" insertion="Effect.blind" * insertionDuration="0.1" string="Contact" action="$hitMe"> * <dl> * <dt>Phone</dt> * <dd>804.555.1212</dd> * <dt>Address</dt> * <dd> * 123 Somewhere Rd.<br /> * Richmond, VA 23233 * </dd> * </dl> * </wo:AjaxExpansion></code> * </pre> * * @author mschrag * @binding id the id of the contents <code>div</code> * @binding linkID the id of the toggle link (defaults to "[id]Link") * @binding class the class of the contents <code>div</code> * @binding linkClass the class of the toggle link (always gets "expansion" * added, and "expanded" when opened) * @binding expanded optionally allows controlling the expansion state of the * contents * @binding initiallyExpanded optionally allows controlling the initial * expansion state when the "expanded" binding is <em>not</em> used * @binding string the string displayed for the link. For something fancier than * a plain string, see above. * @binding openedLabel the string to display when expanded. An alternative to * the <code>string</code> binding. * @binding closedLabel the string to display when not expanded. An alternative * to the <code>string</code> binding. * @binding insertion the insertion effect (see <code>AjaxUpdateLink</code>) * @binding insertionDuration the insertion effect duration (see * <code>AjaxUpdateLink</code>) * @binding action the action to fire when the hyperlink is clicked (that is, on * expansion <em>and</em> contraction) * @binding onLoading JavaScript function to evaluate when the update request * begins * @binding onComplete JavaScript function to evaluate when the update request * has finished * @binding onSuccess JavaScript function to evaluate when the update request * was successful * @binding onFailure JavaScript function to evaluate when the update request * has failed * @binding onException JavaScript function to evaluate when the update request * had errors * @binding accesskey hot key that should toggle the expansion (optional) * @binding onExpansionComplete value for the <code>AjaxUpdateContainer</code> * <code>onRefreshComplete</code> binding when the contents are * expanded */ public class AjaxExpansion extends AjaxComponent { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; /** * ID for {@link AjaxUpdateContainer} */ private String _id; /** * Current expansion state */ private Boolean _expanded; /** * Constructor * * @param context a {@link WOContext} */ public AjaxExpansion(WOContext context) { super(context); } @Override public boolean synchronizesVariablesWithBindings() { return false; } /** * Returns ID for {@link AjaxUpdateLink}. * * @return value of {@code linkID} binding if set, otherwise value of * {@link #id()} with "Link" appended */ public String linkID() { String linkID = (String) valueForBinding("linkID"); if (linkID == null) { linkID = id() + "Link"; } return linkID; } /** * Returns CSS {@code class} attribute value applied to the * {@link AjaxUpdateLink}, and the corresponding HTML hyperlink element. * * @return CSS class */ public String linkClass() { String linkClass = (String) valueForBinding("linkClass"); StringBuilder linkClassBuffer = new StringBuilder(); linkClassBuffer.append("expansion"); if (isExpanded()) { linkClassBuffer.append(" expanded"); } if (linkClass != null) { linkClassBuffer.append(' '); linkClassBuffer.append(linkClass); } return linkClassBuffer.toString(); } /** * Returns ID for {@link AjaxUpdateContainer}. * * @return ID value */ public String id() { if (_id == null) { _id = (String) valueForBinding("id"); if (_id == null) { _id = ERXWOContext.safeIdentifierName(context(), true); } } return _id; } /** * Returns the label to render for the hyperlink, based on the * {@code string} or {@code openedLabel} and {@code closedLabel} bindings. * * @return label to render for the hyperlink */ public String string() { String string = (String) valueForBinding("string"); if (null == string) { if (isExpanded()) { string = (String)valueForBinding("openedLabel"); } else { string = (String)valueForBinding("closedLabel"); } } return string; } /** * Is this request an Ajax request? * * @return {@code true} if this request is an Ajax request, otherwise * {@code false} */ public boolean isAjaxRequest() { return AjaxUtils.isAjaxRequest(context().request()); } /** * Returns an escaped version of {@link #string()} using * {@link er.extensions.foundation.ERXStringUtilities#escapeJavascriptApostrophes(String)}. * * @return escaped string */ public String jsEscapedString() { return ERXStringUtilities.escapeJavascriptApostrophes(string()); } /** * Adds required resources for this component. */ @Override protected void addRequiredWebResources(WOResponse response) { addScriptResourceInHead(response, "prototype.js"); addScriptResourceInHead(response, "effects.js"); addScriptResourceInHead(response, "wonder.js"); } /** * Returns {@code null}. * * @return {@code null} */ @Override public WOActionResults handleRequest(WORequest request, WOContext context) { return null; } /** * Is the component currently expanded? * * @return {@code true} if the component is currently expanded, otherwise * {@code false} */ public boolean isExpanded() { boolean expanded; if (hasBinding("expanded")) { expanded = ((Boolean) valueForBinding("expanded")).booleanValue(); } else { if (_expanded == null) { _expanded = Boolean.valueOf(booleanValueForBinding("initiallyExpanded", false)); } expanded = _expanded.booleanValue(); } return expanded; } /** * Sets the current expansion state of the component to {@code expanded}. * * @param expanded * desired current expansion state */ public void setExpanded(boolean expanded) { Boolean e = Boolean.valueOf(expanded); if (hasBinding("expanded")) { setValueForBinding(e, "expanded"); } else { _expanded = e; } } /** * Toggles the current state of the component. * * @return {@code null} */ public WOActionResults toggle() { setExpanded(!isExpanded()); if (hasBinding("action")) { valueForBinding("action"); } return null; } /** * Returns value of {@code onExpansionComplete} binding, or {@code null} if * the component is not currently expanded. * * @return value of {@code onExpansionComplete} binding, or {@code null} */ public String onExpansionComplete() { if (hasBinding("onExpansionComplete") && isExpanded()) { return (String) valueForBinding("onExpansionComplete"); } return null; } }