package er.ajax;
import com.webobjects.appserver.WOActionResults;
import com.webobjects.appserver.WOAssociation;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WOElement;
import com.webobjects.appserver.WORequest;
import com.webobjects.appserver.WOResponse;
import com.webobjects.appserver._private.WOConstantValueAssociation;
import com.webobjects.appserver._private.WODynamicGroup;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
/**
* This implements the tabs and the main control of a tabbed panel display as an
* unordered list (UL and LI elements). The tab contents go in AjaxTabbedPanel
* components contained within this component. The tab contents are loaded on
* demand so only the selected tab is rendered when the page is displayed. If
* tabs can take a while to load, a hidden div containing a "working" message
* can temporarily replace the panel content while it loads.
*
* <h3>CSS Classes Used by AjaxTabbedPanel and AjaxTabbedPanelTab</h3>
* <table>
* <tr>
* <th>Class Name</th>
* <th>Used For</th>
* </tr>
* <tr>
* <td>ajaxTabbedPanel</td>
* <td>The UL containing the tabs.</td>
* </tr>
* <tr>
* <td>ajaxTabbedPanelTab-selected</td>
* <td>The LI representing the selected tab and the A element that is the clickable title.</td>
* </tr>
* <tr>
* <td>ajaxTabbedPanelTab-unselected</td>
* <td>The LI representing the selected tab(s) and the A element that is the clickable title.</td>
* </tr>
* <tr>
* <td>ajaxTabbedPanelPanes</td>
* <td>The UL containing the panels (panes).</td>
* </tr>
* <tr>
* <td>ajaxTabbedPanelPane-selected</td>
* <td>The LI representing the selected panel.</td>
* </tr>
* <tr>
* <td>ajaxTabbedPanelPane-unselected</td>
* <td>The LI representing the unselected panel(s).</td>
* </tr>
* </table>
*
* @binding id required, String the id of the UL that wraps the tabs
* @binding busyDiv optional, String the id of a div that should be shown when a
* tab is loading
* @binding onLoad optional, String JavaScript to execute after the whole tabbed panel loads
* @binding onSelect optional, String JavaScript to execute after a different tab is selected.
* This will <b>not</b> get called when this is first rendered. Use onLoad if you need that.
*
* @author Chuck Hill
*/
public class AjaxTabbedPanel extends AjaxDynamicElement {
private WOElement content;
private NSMutableArray tabs = new NSMutableArray();
private WOAssociation id;
private WOAssociation busyDiv;
private WOAssociation onLoad;
private WOAssociation onSelect;
public AjaxTabbedPanel(String name, NSDictionary associations, WOElement template) {
super(name, associations, template);
content = template;
id = (WOAssociation) associations.objectForKey("id");
busyDiv = (WOAssociation) associations.objectForKey("busyDiv");
onLoad = (WOAssociation) associations.objectForKey("onLoad");
onSelect = (WOAssociation) associations.objectForKey("onSelect");
findTabs((WODynamicGroup)template);
if (id == null)
{
throw new RuntimeException("id binding is required");
}
}
/**
* Looks through the child components to locate the AjaxTabbedPanelTabs that are controlled by this panel.
* Tabs without an explicit id attributed are assigned a calculated one.
*
* @param template the graph of elements passed to the constructor.
*/
private void findTabs(WODynamicGroup template) {
if (template == null || template.childrenElements() == null) return;
NSArray children = template.childrenElements();
for (int i = 0; i < children.count(); i++) {
WOElement child = (WOElement)children.objectAtIndex(i);
if (child instanceof AjaxTabbedPanelTab) {
AjaxTabbedPanelTab childTab = (AjaxTabbedPanelTab)child;
// The tabs need to have an id attribute so we assign one if needed
if (childTab.id() == null) {
childTab.setParentId(id);
childTab.setTabNumber(new WOConstantValueAssociation("_pane_" + tabs.count()));
}
tabs.addObject(childTab);
}
else if (child instanceof WODynamicGroup) {
findTabs((WODynamicGroup)child);
}
}
}
/**
* Creates the tabs and pane control.
*/
@Override
public void appendToResponse(WOResponse response, WOContext context) {
WOComponent component = context.component();
String idString = (String) id.valueInComponent(component);
if (idString == null) {
throw new RuntimeException("id binding evaluated to null");
}
// UL for tabs
response.appendContentString("<ul class=\"ajaxTabbedPanel\"");
appendTagAttributeToResponse(response, "id", idString);
// Optional JavaScriplets
if (onLoad != null) {
appendTagAttributeToResponse(response, "onLoad", onLoad.valueInComponent(component));
}
if (onSelect != null) {
appendTagAttributeToResponse(response, "onSelect", onSelect.valueInComponent(component));
}
response.appendContentString(">\n");
String paneControlID = idString + "_panecontrol";
for (int i = 0; i < tabs.count(); i++) {
String index = Integer.toString(i);
String tabID = idString + "_tab_" + index;
AjaxTabbedPanelTab tab = (AjaxTabbedPanelTab)tabs.objectAtIndex(i);
if (tab.isVisble(component)) {
boolean isSelectedTab = tab.isSelected(context.component());
String panelTabID = tab.tabIdInComponent(component);
String panelID = panelTabID + "_panel";
response.appendContentString(" <li class=\"ajaxTabbedPanelTab-");
response.appendContentString(isSelectedTab ? "selected" : "unselected");
response.appendContentString("\" ");
appendTagAttributeToResponse(response, "id", tabID);
response.appendContentString(">\n");
response.appendContentString("<a ");
//add the accesskey
if( tab.accesskey() != null ){
String accessKeyStr = tab.accesskey().valueInComponent(component).toString();
appendTagAttributeToResponse(response, "accesskey", accessKeyStr );
}
appendTagAttributeToResponse(response, "id", panelTabID);
response.appendContentString(" href=\"javascript:void(0)\" onclick=\"");
// Load the tab contents
response.appendContentString("AjaxTabbedPanel.loadPanel('");
response.appendContentString(idString);
response.appendContentString("', '");
response.appendContentString(panelID);
response.appendContentString("', '");
response.appendContentString((busyDiv != null) ? (String)busyDiv.valueInComponent(component) : "");
response.appendContentString("', ");
response.appendContentString(tab.refreshesOnSelect(context.component()).toString());
response.appendContentString("); ");
// Select the tab contents
response.appendContentString("AjaxTabbedPanel.selectPanel('");
response.appendContentString(paneControlID);
response.appendContentString("', '");
response.appendContentString(panelID);
response.appendContentString("'); ");
// Select the tab control
response.appendContentString("AjaxTabbedPanel.selectTab('");
response.appendContentString(idString);
response.appendContentString("', '");
response.appendContentString(tabID);
response.appendContentString("', '");
response.appendContentString(panelID);
response.appendContentString("', '");
response.appendContentString((busyDiv != null) ? (String)busyDiv.valueInComponent(component) : "");
response.appendContentString("'); ");
response.appendContentString("\">");
response.appendContentString((String) tab.name().valueInComponent(component));
response.appendContentString("</a>\n");
response.appendContentString("</li>\n");
}
}
response.appendContentString("</ul>\n");
// UL for panes
response.appendContentString("<ul class=\"ajaxTabbedPanelPanes\" ");
appendTagAttributeToResponse(response, "id", paneControlID);
response.appendContentString(">\n");
// The tabs render themselves as panes
if (content != null) {
content.appendToResponse(response, context);
}
response.appendContentString("</ul>\n");
super.appendToResponse(response, context);
response.appendContentString("<script>AjaxTabbedPanel.onLoad('");
response.appendContentString(idString);
response.appendContentString("');</script>\n");
}
@Override
protected void addRequiredWebResources(WOResponse response, WOContext context) {
AjaxUtils.addScriptResourceInHead(context, response, "prototype.js");
// Wonder is not needed by this component, but it is often used when Ajax components are used
// on the tabs. It is included here to make it a little easier to use Ajax components on tabs.
// Otherwise, the page needs to load the needed scripts manually.
AjaxUtils.addScriptResourceInHead(context, response, "wonder.js");
AjaxUtils.addScriptResourceInHead(context, response, "switchtabs.js");
}
@Override
public WOActionResults handleRequest(WORequest request, WOContext context) {
return null;
}
}