You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2007/05/05 20:45:03 UTC

svn commit: r535556 [3/3] - in /struts/struts2/trunk/plugins/dojo/src/main: java/org/apache/struts2/dojo/components/ java/org/apache/struts2/dojo/views/jsp/ui/ resources/org/apache/struts2/static/ resources/org/apache/struts2/static/dojo/ resources/org...

Modified: struts/struts2/trunk/plugins/dojo/src/main/resources/org/apache/struts2/static/dojo/struts_dojo.js.uncompressed.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/dojo/src/main/resources/org/apache/struts2/static/dojo/struts_dojo.js.uncompressed.js?view=diff&rev=535556&r1=535555&r2=535556
==============================================================================
--- struts/struts2/trunk/plugins/dojo/src/main/resources/org/apache/struts2/static/dojo/struts_dojo.js.uncompressed.js (original)
+++ struts/struts2/trunk/plugins/dojo/src/main/resources/org/apache/struts2/static/dojo/struts_dojo.js.uncompressed.js Sat May  5 11:45:02 2007
@@ -22263,7 +22263,7 @@
     if(this.notifyTopicsArray) {
       dojo.lang.forEach(this.notifyTopicsArray, function(topic) {
         try {
-          dojo.event.topic.publish(topic, data, type, e);
+          dojo.event.topic.publish(topic, data, type, e, self);
         } catch(ex){
 		  self.log(ex);
         }
@@ -22274,25 +22274,27 @@
     var topicsArray = null;
     switch(type) {
       case "before":
-        topicsArray = this.beforeNotifyTopicsArray;
+        this.notifyTo(this.beforeNotifyTopicsArray, null, e);
         break;
       case "load":
-        topicsArray = this.afterNotifyTopicsArray;
+        this.notifyTo(this.afterNotifyTopicsArray, data, e);
         break;
       case "error":
-        topicsArray = this.errorNotifyTopicsArray;
+        this.notifyTo(this.errorNotifyTopicsArray, data, e);
         break;
     }
-    
-    this.notifyTo(topicsArray, data, type, e);
   },
   
-  notifyTo : function(topicsArray, data, type, e) {
+  notifyTo : function(topicsArray, data, e) {
     var self = this;
     if(topicsArray) {
       dojo.lang.forEach(topicsArray, function(topic) {
       try {
-        dojo.event.topic.publish(topic, data, type, e);
+        if(data) {
+          dojo.event.topic.publish(topic, data, e, self);
+        } else {
+          dojo.event.topic.publish(topic, e, self);
+        }
       } catch(ex){
         self.log(ex);
       }
@@ -22537,6 +22539,9 @@
     highlightColor : "",
     highlightDuration : 2000,
     
+    //only used when inside a tabbedpanel
+    disabled : false,
+    
     onDownloadStart : function(event) {
       if(!this.showLoading) {
         event.returnValue = false;
@@ -22581,7 +22586,7 @@
         var self = this;
         dojo.lang.forEach(this.notifyTopicsArray, function(topic) {
           try {
-            dojo.event.topic.publish(topic, data, type, e);
+            dojo.event.topic.publish(topic, data, type, e, self);
           } catch(ex) {
             self.log(ex);
           }
@@ -22592,28 +22597,30 @@
       var topicsArray = null;
       switch(type) {
         case "before":
-          topicsArray = this.beforeNotifyTopicsArray;
+          this.notifyTo(this.beforeNotifyTopicsArray, null, e);
           break;
         case "load":
-          topicsArray = this.afterNotifyTopicsArray;
+          this.notifyTo(this.afterNotifyTopicsArray, data, e);
           break;
         case "error":
-          topicsArray = this.errorNotifyTopicsArray;
+          this.notifyTo(this.errorNotifyTopicsArray, data, e);
           break;
       }
-    
-      this.notifyTo(topicsArray, data, type, e);
     },
     
-    notifyTo : function(topicsArray, data, type, e) {
+    notifyTo : function(topicsArray, data, e) {
       var self = this;
       if(topicsArray) {
         dojo.lang.forEach(topicsArray, function(topic) {
-          try {
-            dojo.event.topic.publish(topic, data, type, e);
-          } catch(ex){
-            self.log(ex);
+        try {
+          if(data) {
+            dojo.event.topic.publish(topic, data, e, self);
+          } else {
+            dojo.event.topic.publish(topic, e, self);
           }
+        } catch(ex){
+          self.log(ex);
+        }
         });
       }
     },
@@ -28107,6 +28114,723 @@
   
 });
 
+dojo.provide("dojo.widget.PageContainer");
+
+
+
+
+
+
+dojo.widget.defineWidget("dojo.widget.PageContainer", dojo.widget.HtmlWidget, {
+	// summary
+	//	A container that has multiple children, but shows only
+	//	one child at a time (like looking at the pages in a book one by one).
+	//
+	//	Publishes topics <widgetId>-addChild, <widgetId>-removeChild, and <widgetId>-selectChild
+	//
+	//	Can be base class for container, Wizard, Show, etc.
+
+	isContainer: true,
+
+	// doLayout: Boolean
+	//  if true, change the size of my currently displayed child to match my size
+	doLayout: true,
+
+	templateString: "<div dojoAttachPoint='containerNode'></div>",
+
+	// selectedChild: String
+	//   id of the currently shown page
+	selectedChild: "",
+
+	fillInTemplate: function(args, frag) {
+		// Copy style info from input node to output node
+		var source = this.getFragNodeRef(frag);
+		dojo.html.copyStyle(this.domNode, source);
+		dojo.widget.PageContainer.superclass.fillInTemplate.apply(this, arguments);
+	},
+
+	postCreate: function(args, frag) {
+		if(this.children.length){
+			// Setup each page panel
+			dojo.lang.forEach(this.children, this._setupChild, this);
+
+			// Figure out which child to initially display
+			var initialChild;
+			if(this.selectedChild){
+				this.selectChild(this.selectedChild);
+			}else{
+				for(var i=0; i<this.children.length; i++){
+					if(this.children[i].selected){
+						this.selectChild(this.children[i]);
+						break;
+					}
+				}
+				if(!this.selectedChildWidget){
+					this.selectChild(this.children[0]);
+				}
+			}
+		}
+	},
+
+	addChild: function(child){
+		dojo.widget.PageContainer.superclass.addChild.apply(this, arguments);
+		this._setupChild(child);
+
+		// in case the tab labels have overflowed from one line to two lines
+		this.onResized();
+
+		// if this is the first child, then select it
+		if(!this.selectedChildWidget){
+			this.selectChild(child);
+		}
+	},
+
+	_setupChild: function(/*Widget*/ page){
+		// Summary: Add the given child to this page container
+
+		page.hide();
+		
+		// since we are setting the width/height of the child elements, they need
+		// to be position:relative, or IE has problems (See bug #2033)
+		page.domNode.style.position="relative";
+
+		// publish the addChild event for panes added via addChild(), and the original panes too
+		dojo.event.topic.publish(this.widgetId+"-addChild", page);
+	},
+
+	removeChild: function(/*Widget*/ page){
+		dojo.widget.PageContainer.superclass.removeChild.apply(this, arguments);
+
+		// If we are being destroyed than don't run the code below (to select another page), because we are deleting
+		// every page one by one
+		if(this._beingDestroyed){ return; }
+
+		// this will notify any tablists to remove a button; do this first because it may affect sizing
+		dojo.event.topic.publish(this.widgetId+"-removeChild", page);
+
+		// in case the tab labels now take up one line instead of two lines
+		this.onResized();
+
+		if (this.selectedChildWidget === page) {
+			this.selectedChildWidget = undefined;
+			if (this.children.length > 0) {
+				this.selectChild(this.children[0], true);
+			}
+		}
+	},
+
+	selectChild: function(/*Widget*/ page, /*Widget*/ callingWidget){
+		// summary
+		//	Show the given widget (which must be one of my children)
+		page = dojo.widget.byId(page);
+		this.correspondingPageButton = callingWidget;
+
+		// Deselect old page and select new one
+		if(this.selectedChildWidget){
+			this._hideChild(this.selectedChildWidget);
+		}
+		this.selectedChildWidget = page;
+		this.selectedChild = page.widgetId;
+		this._showChild(page);
+		page.isFirstChild = (page == this.children[0]);
+		page.isLastChild = (page == this.children[this.children.length-1]);
+		dojo.event.topic.publish(this.widgetId+"-selectChild", page);
+	},
+
+	forward: function(){
+		// Summary: advance to next page
+		var index = dojo.lang.find(this.children, this.selectedChildWidget);
+		this.selectChild(this.children[index+1]);
+	},
+
+	back: function(){
+		// Summary: go back to previous page
+		var index = dojo.lang.find(this.children, this.selectedChildWidget);
+		this.selectChild(this.children[index-1]);
+	},
+
+	onResized: function(){
+		// Summary: called when any page is shown, to make it fit the container correctly
+		if(this.doLayout && this.selectedChildWidget){
+			with(this.selectedChildWidget.domNode.style){
+				top = dojo.html.getPixelValue(this.containerNode, "padding-top", true);
+				left = dojo.html.getPixelValue(this.containerNode, "padding-left", true);
+			}
+			var content = dojo.html.getContentBox(this.containerNode);
+			this.selectedChildWidget.resizeTo(content.width, content.height);
+		}
+	},
+
+	_showChild: function(/*Widget*/ page) {
+		// size the current page (in case this is the first time it's being shown, or I have been resized)
+		if(this.doLayout){
+			var content = dojo.html.getContentBox(this.containerNode);
+			page.resizeTo(content.width, content.height);
+		}
+
+		page.selected=true;
+		page.show();
+	},
+
+	_hideChild: function(/*Widget*/ page) {
+		page.selected=false;
+		page.hide();
+	},
+
+	closeChild: function(/*Widget*/ page) {
+		// summary
+		//	callback when user clicks the [X] to remove a page
+		//	if onClose() returns true then remove and destroy the childd
+		var remove = page.onClose(this, page);
+		if(remove) {
+			this.removeChild(page);
+			// makes sure we can clean up executeScripts in ContentPane onUnLoad
+			page.destroy();
+		}
+	},
+
+	destroy: function(){
+		this._beingDestroyed = true;
+		dojo.event.topic.destroy(this.widgetId+"-addChild");
+		dojo.event.topic.destroy(this.widgetId+"-removeChild");
+		dojo.event.topic.destroy(this.widgetId+"-selectChild");
+		dojo.widget.PageContainer.superclass.destroy.apply(this, arguments);
+	}
+});
+
+
+dojo.widget.defineWidget(
+    "dojo.widget.PageController",
+    dojo.widget.HtmlWidget,
+	{
+		// summary
+		//	Set of buttons to select a page in a page list.
+		//	Monitors the specified PageContaine, and whenever a page is
+		//	added, deleted, or selected, updates itself accordingly.
+
+		templateString: "<span wairole='tablist' dojoAttachEvent='onKey'></span>",
+		isContainer: true,
+
+		// containerId: String
+		//	the id of the page container that I point to
+		containerId: "",
+
+		// buttonWidget: String
+		//	the name of the button widget to create to correspond to each page
+		buttonWidget: "PageButton",
+
+		// class: String
+		//	Class name to apply to the top dom node
+		"class": "dojoPageController",
+
+		fillInTemplate: function() {
+			dojo.html.addClass(this.domNode, this["class"]);  // "class" is a reserved word in JS
+			dojo.widget.wai.setAttr(this.domNode, "waiRole", "role", "tablist");
+		},
+
+		postCreate: function(){
+			this.pane2button = {};		// mapping from panes to buttons
+
+			// If children have already been added to the page container then create buttons for them
+			var container = dojo.widget.byId(this.containerId);
+			if(container){
+				dojo.lang.forEach(container.children, this.onAddChild, this);
+			}
+
+			dojo.event.topic.subscribe(this.containerId+"-addChild", this, "onAddChild");
+			dojo.event.topic.subscribe(this.containerId+"-removeChild", this, "onRemoveChild");
+			dojo.event.topic.subscribe(this.containerId+"-selectChild", this, "onSelectChild");
+		},
+
+		destroy: function(){
+			dojo.event.topic.unsubscribe(this.containerId+"-addChild", this, "onAddChild");
+			dojo.event.topic.unsubscribe(this.containerId+"-removeChild", this, "onRemoveChild");
+			dojo.event.topic.unsubscribe(this.containerId+"-selectChild", this, "onSelectChild");
+			dojo.widget.PageController.superclass.destroy.apply(this, arguments);
+		},
+
+		onAddChild: function(/*Widget*/ page){
+			// summary
+			//   Called whenever a page is added to the container.
+			//   Create button corresponding to the page.
+			var button = dojo.widget.createWidget(this.buttonWidget,
+				{
+					label: page.label,
+					closeButton: page.closable
+				});
+			this.addChild(button);
+			this.domNode.appendChild(button.domNode);
+			this.pane2button[page]=button;
+			page.controlButton = button;	// this value might be overwritten if two tabs point to same container
+
+			var _this = this;
+			dojo.event.connect(button, "onClick", function(){ _this.onButtonClick(page); });
+			dojo.event.connect(button, "onCloseButtonClick", function(){ _this.onCloseButtonClick(page); });
+		},
+
+		onRemoveChild: function(/*Widget*/ page){
+			// summary
+			//   Called whenever a page is removed from the container.
+			//   Remove the button corresponding to the page.
+			if(this._currentChild == page){ this._currentChild = null; }
+			var button = this.pane2button[page];
+			if(button){
+				button.destroy();
+			}
+			this.pane2button[page] = null;
+		},
+
+		onSelectChild: function(/*Widget*/ page){
+			// Summary
+			//	Called when a page has been selected in the PageContainer, either by me or by another PageController
+			if(this._currentChild){
+				var oldButton=this.pane2button[this._currentChild];
+				oldButton.clearSelected();
+			}
+			var newButton=this.pane2button[page];
+			newButton.setSelected();
+			this._currentChild=page;
+		},
+
+		onButtonClick: function(/*Widget*/ page){
+			// summary
+			//   Called whenever one of my child buttons is pressed in an attempt to select a page
+			var container = dojo.widget.byId(this.containerId);	// TODO: do this via topics?
+			container.selectChild(page, false, this);
+		},
+
+		onCloseButtonClick: function(/*Widget*/ page){
+			// summary
+			//   Called whenever one of my child buttons [X] is pressed in an attempt to close a page
+			var container = dojo.widget.byId(this.containerId);
+			container.closeChild(page);
+		},
+
+		onKey: function(/*Event*/ evt){
+			// summary:
+			//   Handle keystrokes on the page list, for advancing to next/previous button
+
+			if( (evt.keyCode == evt.KEY_RIGHT_ARROW)||
+				(evt.keyCode == evt.KEY_LEFT_ARROW) ){
+				var current = 0;
+				var next = null;	// the next button to focus on
+				
+				// find currently focused button in children array
+				var current = dojo.lang.find(this.children, this.pane2button[this._currentChild]);
+				
+				// pick next button to focus on
+				if(evt.keyCode == evt.KEY_RIGHT_ARROW){
+					next = this.children[ (current+1) % this.children.length ]; 
+				}else{ // is LEFT_ARROW
+					next = this.children[ (current+ (this.children.length-1)) % this.children.length ];
+				}
+				
+				dojo.event.browser.stopEvent(evt);
+				next.onClick();
+			}
+		}
+	}
+);
+
+dojo.widget.defineWidget("dojo.widget.PageButton", dojo.widget.HtmlWidget,
+{
+	// summary
+	//	Internal widget used by PageList.
+	//	The button-like or tab-like object you click to select or delete a page
+
+	templateString: "<span class='item'>" +
+						"<span dojoAttachEvent='onClick' dojoAttachPoint='titleNode' class='selectButton'>${this.label}</span>" +
+						"<span dojoAttachEvent='onClick:onCloseButtonClick' class='closeButton'>[X]</span>" +
+					"</span>",
+
+	// label: String
+	//  Name to print on the button
+	label: "foo",
+	
+	// closeButton: Boolean
+	//	true iff we should also print a close icon to destroy corresponding page
+	closeButton: false,
+
+	onClick: function(){
+		// summary
+		//  Basically this is the attach point PageController listens to, to select the page
+		this.focus();
+	},
+
+	onCloseButtonMouseOver: function(){
+		// summary
+		//	The close button changes color a bit when you mouse over	
+		dojo.html.addClass(this.closeButtonNode, "closeHover");
+	},
+
+	onCloseButtonMouseOut: function(){
+		// summary
+		// 	Revert close button to normal color on mouse out
+		dojo.html.removeClass(this.closeButtonNode, "closeHover");
+	},
+
+	onCloseButtonClick: function(/*Event*/ evt){
+		// summary
+		//	Handle clicking the close button for this tab
+	},
+	
+	setSelected: function(){
+		// summary
+		//	This is run whenever the page corresponding to this button has been selected
+		dojo.html.addClass(this.domNode, "current");
+		this.titleNode.setAttribute("tabIndex","0");
+	},
+	
+	clearSelected: function(){
+		// summary
+		//	This function is run whenever the page corresponding to this button has been deselected (and another page has been shown)
+		dojo.html.removeClass(this.domNode, "current");
+		this.titleNode.setAttribute("tabIndex","-1");
+	},
+
+	focus: function(){
+		// summary
+		//	This will focus on the this button (for accessibility you need to do this when the button is selected)
+		if(this.titleNode.focus){	// mozilla 1.7 doesn't have focus() func
+			this.titleNode.focus();
+		}
+	}
+});
+
+// These arguments can be specified for the children of a PageContainer.
+// Since any widget can be specified as a PageContainer child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	// label: String
+	//		Label or title of this widget.  Used by TabContainer to the name the tab, etc.
+	label: "",
+	
+	// selected: Boolean
+	//		Is this child currently selected?
+	selected: false,
+	
+	// closable: Boolean
+	//		True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
+	closable: false,	// true if user can close this tab pane
+	
+	onClose: function(){
+		// summary: Callback if someone tries to close the child, child will be closed if func returns true
+		return true;
+	}
+});
+
+dojo.provide("dojo.widget.TabContainer");
+
+
+
+
+
+
+
+
+dojo.widget.defineWidget("dojo.widget.TabContainer", dojo.widget.PageContainer, {
+
+	// summary
+	//	A TabContainer is a container that has multiple panes, but shows only
+	//	one pane at a time.  There are a set of tabs corresponding to each pane,
+	//	where each tab has the title (aka label) of the pane, and optionally a close button.
+	//
+	//	Publishes topics <widgetId>-addChild, <widgetId>-removeChild, and <widgetId>-selectChild
+	//	(where <widgetId> is the id of the TabContainer itself.
+
+	// labelPosition: String
+	//   Defines where tab labels go relative to tab content.
+	//   "top", "bottom", "left-h", "right-h"
+	labelPosition: "top",
+	
+	// closeButton: String
+	//   If closebutton=="tab", then every tab gets a close button.
+	//   DEPRECATED:  Should just say closable=true on each
+	//   pane you want to be closable.
+	closeButton: "none",
+
+	templateString: null,	// override setting in PageContainer
+	templatePath: dojo.uri.moduleUri("dojo.widget", "templates/TabContainer.html"),
+	templateCssPath: dojo.uri.moduleUri("dojo.widget", "templates/TabContainer.css"),
+
+	// selectedTab: String
+	//	initially selected tab (widgetId)
+	//	DEPRECATED: use selectedChild instead.
+	selectedTab: "",
+
+	postMixInProperties: function() {
+		if(this.selectedTab){
+			dojo.deprecated("selectedTab deprecated, use selectedChild instead, will be removed in", "0.5");
+			this.selectedChild=this.selectedTab;
+		}
+		if(this.closeButton!="none"){
+			dojo.deprecated("closeButton deprecated, use closable='true' on each child instead, will be removed in", "0.5");
+		}
+		dojo.widget.TabContainer.superclass.postMixInProperties.apply(this, arguments);
+	},
+
+	fillInTemplate: function() {
+		// create the tab list that will have a tab (a.k.a. tab button) for each tab panel
+		this.tablist = dojo.widget.createWidget("TabController",
+			{
+				id: this.widgetId + "_tablist",
+				labelPosition: this.labelPosition,
+				doLayout: this.doLayout,
+				containerId: this.widgetId
+			}, this.tablistNode);
+		dojo.widget.TabContainer.superclass.fillInTemplate.apply(this, arguments);
+	},
+
+	postCreate: function(args, frag) {	
+		dojo.widget.TabContainer.superclass.postCreate.apply(this, arguments);
+
+		// size the container pane to take up the space not used by the tabs themselves
+		this.onResized();
+	},
+
+	_setupChild: function(tab){
+		if(this.closeButton=="tab" || this.closeButton=="pane"){
+			// TODO: remove in 0.5
+			tab.closable=true;
+		}
+		dojo.html.addClass(tab.domNode, "dojoTabPane");
+		dojo.widget.TabContainer.superclass._setupChild.apply(this, arguments);
+	},
+
+	onResized: function(){
+		// Summary: Configure the content pane to take up all the space except for where the tabs are
+		if(!this.doLayout){ return; }
+
+		// position the labels and the container node
+		var labelAlign=this.labelPosition.replace(/-h/,"");
+		var children = [
+			{domNode: this.tablist.domNode, layoutAlign: labelAlign},
+			{domNode: this.containerNode, layoutAlign: "client"}
+		];
+		dojo.widget.html.layout(this.domNode, children);
+
+		if(this.selectedChildWidget){
+			var containerSize = dojo.html.getContentBox(this.containerNode);
+			this.selectedChildWidget.resizeTo(containerSize.width, containerSize.height);
+		}
+	},
+
+	selectTab: function(tab, callingWidget){
+		dojo.deprecated("use selectChild() rather than selectTab(), selectTab() will be removed in", "0.5");
+		this.selectChild(tab, callingWidget);
+	},
+
+	onKey: function(e){
+		// summary
+		//	Keystroke handling for keystrokes on the tab panel itself (that were bubbled up to me)
+		//	Ctrl-up: focus is returned from the pane to the tab button
+		//	Alt-del: close tab
+		if(e.keyCode == e.KEY_UP_ARROW && e.ctrlKey){
+			// set focus to current tab
+			var button = this.correspondingTabButton || this.selectedTabWidget.tabButton;
+			button.focus();
+			dojo.event.browser.stopEvent(e);
+		}else if(e.keyCode == e.KEY_DELETE && e.altKey){
+			if (this.selectedChildWidget.closable){
+				this.closeChild(this.selectedChildWidget);
+				dojo.event.browser.stopEvent(e);
+			}
+		}
+	},
+
+	destroy: function(){
+		this.tablist.destroy();
+		dojo.widget.TabContainer.superclass.destroy.apply(this, arguments);
+	}
+});
+
+dojo.widget.defineWidget(
+    "dojo.widget.TabController",
+    dojo.widget.PageController,
+	{
+		// summary
+		// 	Set of tabs (the things with labels and a close button, that you click to show a tab panel).
+		//	Lets the user select the currently shown pane in a TabContainer or PageContainer.
+		//	TabController also monitors the TabContainer, and whenever a pane is
+		//	added or deleted updates itself accordingly.
+
+		templateString: "<div wairole='tablist' dojoAttachEvent='onKey'></div>",
+
+		// labelPosition: String
+		//   Defines where tab labels go relative to tab content.
+		//   "top", "bottom", "left-h", "right-h"
+		labelPosition: "top",
+
+		doLayout: true,
+
+		// class: String
+		//	Class name to apply to the top dom node
+		"class": "",
+
+		// buttonWidget: String
+		//	the name of the tab widget to create to correspond to each page
+		buttonWidget: "TabButton",
+
+		postMixInProperties: function() {
+			if(!this["class"]){
+				this["class"] = "dojoTabLabels-" + this.labelPosition + (this.doLayout ? "" : " dojoTabNoLayout");
+			}
+			dojo.widget.TabController.superclass.postMixInProperties.apply(this, arguments);
+		}
+	}
+);
+
+dojo.widget.defineWidget("dojo.widget.TabButton", dojo.widget.PageButton,
+{
+	// summary
+	//	A tab (the thing you click to select a pane).
+	//	Contains the title (aka label) of the pane, and optionally a close-button to destroy the pane.
+	//	This is an internal widget and should not be instantiated directly.
+
+	templateString: "<div class='dojoTab' dojoAttachEvent='onClick'>"
+						+"<div dojoAttachPoint='innerDiv'>"
+							+"<span dojoAttachPoint='titleNode' tabIndex='-1' waiRole='tab'>${this.label}</span>"
+							+"<span dojoAttachPoint='closeButtonNode' class='close closeImage' style='${this.closeButtonStyle}'"
+							+"    dojoAttachEvent='onMouseOver:onCloseButtonMouseOver; onMouseOut:onCloseButtonMouseOut; onClick:onCloseButtonClick'></span>"
+						+"</div>"
+					+"</div>",
+
+	postMixInProperties: function(){
+		this.closeButtonStyle = this.closeButton ? "" : "display: none";
+		dojo.widget.TabButton.superclass.postMixInProperties.apply(this, arguments);
+	},
+
+	fillInTemplate: function(){
+		dojo.html.disableSelection(this.titleNode);
+		dojo.widget.TabButton.superclass.fillInTemplate.apply(this, arguments);
+	},
+	
+	onCloseButtonClick: function(/*Event*/ evt){
+		// since the close button is located inside the select button, make sure that the select
+		// button doesn't inadvertently get an onClick event
+		evt.stopPropagation();
+		dojo.widget.TabButton.superclass.onCloseButtonClick.apply(this, arguments);
+	}
+});
+
+
+dojo.widget.defineWidget(
+	"dojo.widget.a11y.TabButton",
+	dojo.widget.TabButton,
+	{
+		// summary
+		//	Tab for display in high-contrast mode (where background images don't show up).
+		//	This is an internal widget and shouldn't be instantiated directly.
+
+		imgPath: dojo.uri.moduleUri("dojo.widget", "templates/images/tab_close.gif"),
+		
+		templateString: "<div class='dojoTab' dojoAttachEvent='onClick;onKey'>"
+							+"<div dojoAttachPoint='innerDiv'>"
+								+"<span dojoAttachPoint='titleNode' tabIndex='-1' waiRole='tab'>${this.label}</span>"
+								+"<img class='close' src='${this.imgPath}' alt='[x]' style='${this.closeButtonStyle}'"
+								+"    dojoAttachEvent='onClick:onCloseButtonClick'>"
+							+"</div>"
+						+"</div>"
+	}
+);
+
+dojo.provide("struts.widget.StrutsTabContainer");
+
+
+
+dojo.widget.defineWidget(
+  "struts.widget.StrutsTabContainer",
+  dojo.widget.TabContainer, {
+  widgetType : "StrutsTabContainer",
+
+  afterSelectTabNotifyTopics : "",
+  afterSelectTabNotifyTopicsArray : null,
+  beforeSelectTabNotifyTopics : "",
+  beforeSelectTabNotifyTopicsArray : null,
+
+  disabledTabCssClass : "strutsDisabledTab",
+  
+  postCreate : function() {
+    struts.widget.StrutsTabContainer.superclass.postCreate.apply(this);
+    
+    //before topics
+    if(!dojo.string.isBlank(this.beforeSelectTabNotifyTopics)) {
+      this.beforeSelectTabNotifyTopicsArray = this.beforeSelectTabNotifyTopics.split(",");
+    }
+    
+    //after topics
+    if(!dojo.string.isBlank(this.afterSelectTabNotifyTopics)) {
+      this.afterSelectTabNotifyTopicsArray = this.afterSelectTabNotifyTopics.split(",");
+    }
+    
+    // add disabled class to disabled tabs
+    if(this.disabledTabCssClass) {
+      dojo.lang.forEach(this.children, function(div){
+        if(div.disabled) {
+          this.disableTab(div);
+        }
+      });
+    }
+  },
+   
+  selectChild: function (tab, callingWidget)  {
+    if(!tab.disabled) {
+      var cancel = {"cancel" : false};
+      
+      if(this.beforeSelectTabNotifyTopicsArray) {
+        var self = this;
+        dojo.lang.forEach(this.beforeSelectTabNotifyTopicsArray, function(topic) {
+          try {
+            dojo.event.topic.publish(topic, self, tab, cancel);
+          } catch(ex){
+            dojo.debug(ex);
+          }
+        });   
+      }
+      
+      if(!cancel.cancel) {
+        struts.widget.StrutsTabContainer.superclass.selectChild.apply(this, [tab, callingWidget]);
+        
+        if(this.afterSelectTabNotifyTopicsArray) {
+          var self = this;
+          dojo.lang.forEach(this.afterSelectTabNotifyTopicsArray, function(topic) {
+            try {
+              dojo.event.topic.publish(topic, self, tab, cancel);
+            } catch(ex){
+              dojo.debug(ex);
+            }
+          });   
+        }
+      } 
+    } 
+  },
+  
+  disableTab : function(t) {
+    var tabWidget = this.getTabWidget(t);
+    tabWidget.disabled = true;
+    dojo.html.addClass(tabWidget.controlButton.domNode, this.disabledTabCssClass);
+  },
+  
+  enableTab : function(t) {
+    var tabWidget = this.getTabWidget(t);
+    tabWidget.disabled = false;
+    dojo.html.removeClass(tabWidget.controlButton.domNode, this.disabledTabCssClass);
+  },
+  
+  getTabWidget : function(t) {
+    if(dojo.lang.isNumber(t)) {
+      //tab index
+      return this.children[t];
+    } else if(dojo.lang.isString(t)) {
+      //tab id
+      return dojo.widget.byId(t);
+    } else {
+      //tab widget?
+      return t;
+    }
+  }
+});
+
 dojo.kwCompoundRequire({
 	common: ["struts.widget.Bind",
 	         "struts.widget.BindDiv",
@@ -28115,7 +28839,8 @@
              "struts.widget.StrutsTimePicker",
              "struts.widget.StrutsDatePicker",
              "struts.widget.BindEvent",
-             "struts.widget.StrutsTreeSelector"]
+             "struts.widget.StrutsTreeSelector",
+             "struts.widget.StrutsTabContainer"]
 });
 dojo.provide("struts.widget.*");
 

Modified: struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/div.ftl
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/div.ftl?view=diff&rev=535556&r1=535555&r2=535556
==============================================================================
--- struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/div.ftl (original)
+++ struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/div.ftl Sat May  5 11:45:02 2007
@@ -26,6 +26,9 @@
   <#if parameters.preload?exists>
     preload="${parameters.preload?string?html}"<#rt/>
   </#if>
+  <#if parameters.disabled?exists>
+    disabled="${parameters.disabled?string?html}"<#rt/>
+  </#if>
   <#include "/${parameters.templateDir}/ajax/ajax-common.ftl" />
   <#include "/${parameters.templateDir}/simple/scripting-events.ftl" />
   <#include "/${parameters.templateDir}/simple/common-attributes.ftl" />

Modified: struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/tabbedpanel.ftl
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/tabbedpanel.ftl?view=diff&rev=535556&r1=535555&r2=535556
==============================================================================
--- struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/tabbedpanel.ftl (original)
+++ struts/struts2/trunk/plugins/dojo/src/main/resources/template/ajax/tabbedpanel.ftl Sat May  5 11:45:02 2007
@@ -1,3 +1,4 @@
+<link rel="stylesheet" href="${base}/struts/TabbedPanel.css" type="text/css"/>
 <div dojoType="struts:StrutsTabContainer"
   <#if parameters.cssStyle?if_exists != "">
     style="${parameters.cssStyle?html}"<#rt/>
@@ -31,6 +32,9 @@
   </#if>
   <#if parameters.afterSelectTabNotifyTopics?if_exists != "">
     afterSelectTabNotifyTopics="${parameters.afterSelectTabNotifyTopics?html}"<#rt/>
+  </#if>
+  <#if parameters.disabledTabCssClass?if_exists != "">
+    disabledTabCssClass="${parameters.disabledTabCssClass?html}"<#rt/>
   </#if>
   <#include "/${parameters.templateDir}/simple/scripting-events.ftl" />
   <#include "/${parameters.templateDir}/simple/common-attributes.ftl" />