You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by jk...@apache.org on 2006/03/11 22:19:18 UTC

svn commit: r385171 [18/24] - in /jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo: ./ src/ src/animation/ src/collections/ src/crypto/ src/data/ src/dnd/ src/event/ src/flash/ src/flash/flash6/ src/flash/flash8/ src/fx/ src/grap...

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,141 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+// FIXME: not yet functional
+
+dojo.provide("dojo.widget.SvgButton");
+
+dojo.require("dojo.widget.Button");
+
+dojo.widget.SvgButton = function(){
+	// FIXME: this is incomplete and doesn't work yet
+	// if DOMButton turns into a mixin, we should subclass Button instead and
+	// just mix in the DOMButton properties.
+
+	dojo.widget.DomButton.call(this);
+	dojo.widget.SvgWidget.call(this);
+
+	// FIXME: freaking implement this already!
+	this.onFoo = function(){ alert("bar"); }
+
+	this.label = "huzzah!";
+
+	this.setLabel = function(x, y, textSize, label, shape){
+		//var labelNode = this.domNode.ownerDocument.createTextNode(this.label);
+		//var textNode = this.domNode.ownerDocument.createElement("text");
+		var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape);
+		var textString = "";
+		switch(shape) {
+			case "ellipse":
+				textString = "<text x='"+ coords[6] + "' y='"+ coords[7] + "'>"+ label + "</text>";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+			case "rectangle":
+				//FIXME: implement
+				textString = "";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+			case "circle":
+				//FIXME: implement
+				textString = "";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+		}
+		//textNode.appendChild(labelNode);
+		//this.domNode.appendChild(textNode);
+		return textString;
+		alert(textNode.getComputedTextLength());
+	}
+
+	this.fillInTemplate = function(x, y, textSize, label, shape){
+		// the idea is to set the text to the appropriate place given its length
+		// and the template shape
+		
+		// FIXME: For now, assuming text sizes are integers in SVG units
+		this.textSize = textSize || 12;
+		this.label = label;
+		// FIXEME: for now, I'm going to fake this... need to come up with a real way to 
+		// determine the actual width of the text, such as computedStyle
+		var textWidth = this.label.length*this.textSize ;
+		//this.setLabel();
+	}
+}
+
+dojo.inherits(dojo.widget.SvgButton, dojo.widget.DomButton);
+
+// FIXME
+dojo.widget.SvgButton.prototype.shapeString = function(x, y, textSize, label, shape) {
+	switch(shape) {
+		case "ellipse":
+			var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape)
+			return "<ellipse cx='"+ coords[4]+"' cy='"+ coords[5]+"' rx='"+ coords[2]+"' ry='"+ coords[3]+"'/>";
+			break;
+		case "rect":
+			//FIXME: implement
+			return "";
+			//return "<rect x='110' y='45' width='70' height='30'/>";
+			break;
+		case "circle":
+			//FIXME: implement
+			return "";
+			//return "<circle cx='210' cy='60' r='23'/>";
+			break;
+	}
+}
+
+dojo.widget.SvgButton.prototype.coordinates = function(x, y, textSize, label, shape) {
+	switch(shape) {
+		case "ellipse":
+			var buttonWidth = label.length*textSize;
+			var buttonHeight = textSize*2.5
+			var rx = buttonWidth/2;
+			var ry = buttonHeight/2;
+			var cx = rx + x;
+			var cy = ry + y;
+			var textX = cx - rx*textSize/25;
+			var textY = cy*1.1;
+			return [buttonWidth, buttonHeight, rx, ry, cx, cy, textX, textY];
+			break;
+		case "rectangle":
+			//FIXME: implement
+			return "";
+			break;
+		case "circle":
+			//FIXME: implement
+			return "";
+			break;
+	}
+}
+
+dojo.widget.SvgButton.prototype.labelString = function(x, y, textSize, label, shape){
+	var textString = "";
+	var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape);
+	switch(shape) {
+		case "ellipse":
+			textString = "<text x='"+ coords[6] + "' y='"+ coords[7] + "'>"+ label + "</text>";
+			break;
+		case "rectangle":
+			//FIXME: implement
+			textString = "";
+			break;
+		case "circle":
+			//FIXME: implement
+			textString = "";
+			break;
+	}
+	return textString;
+}
+
+dojo.widget.SvgButton.prototype.templateString = function(x, y, textSize, label, shape) {
+	return "<g class='dojoButton' dojoAttachEvent='onClick; onMouseMove: onFoo;' dojoAttachPoint='labelNode'>"+ dojo.widgets.SVGButton.prototype.shapeString(x, y, textSize, label, shape) + dojo.widget.SVGButton.prototype.labelString(x, y, textSize, label, shape) + "</g>";
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,92 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.require("dojo.widget.DomWidget");
+dojo.provide("dojo.widget.SvgWidget");
+dojo.provide("dojo.widget.SVGWidget"); // back compat
+
+dojo.require("dojo.dom");
+
+// SVGWidget is a mixin ONLY
+dojo.widget.SvgWidget = function(args){
+	// mix in the parent type
+	// dojo.widget.DomWidget.call(this);
+}
+dojo.inherits(dojo.widget.SvgWidget, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.SvgWidget, {
+	getContainerHeight: function(){
+		// NOTE: container height must be returned as the INNER height
+		dj_unimplemented("dojo.widget.SvgWidget.getContainerHeight");
+	},
+
+	getContainerWidth: function(){
+		// return this.parent.domNode.offsetWidth;
+		dj_unimplemented("dojo.widget.SvgWidget.getContainerWidth");
+	},
+
+	setNativeHeight: function(height){
+		// var ch = this.getContainerHeight();
+		dj_unimplemented("dojo.widget.SVGWidget.setNativeHeight");
+	},
+
+	createNodesFromText: function(txt, wrap){
+		return dojo.dom.createNodesFromText(txt, wrap);
+	}
+});
+
+dojo.widget.SVGWidget = dojo.widget.SvgWidget;
+
+try{
+(function(){
+	var tf = function(){
+		// FIXME: fill this in!!!
+		var rw = new function(){
+			dojo.widget.SvgWidget.call(this);
+			this.buildRendering = function(){ return; }
+			this.destroyRendering = function(){ return; }
+			this.postInitialize = function(){ return; }
+			this.cleanUp = function(){ return; }
+			this.widgetType = "SVGRootWidget";
+			this.domNode = document.documentElement;
+		}
+		var wm = dojo.widget.manager;
+		wm.root = rw;
+		wm.add(rw);
+
+		// extend the widgetManager with a getWidgetFromNode method
+		wm.getWidgetFromNode = function(node){
+			var filter = function(x){
+				if(x.domNode == node){
+					return true;
+				}
+			}
+			var widgets = [];
+			while((node)&&(widgets.length < 1)){
+				widgets = this.getWidgetsByFilter(filter);
+				node = node.parentNode;
+			}
+			if(widgets.length > 0){
+				return widgets[0];
+			}else{
+				return null;
+			}
+		}
+
+		wm.getWidgetFromEvent = function(domEvt){
+			return this.getWidgetFromNode(domEvt.target);
+		}
+
+		wm.getWidgetFromPrimitive = wm.getWidgetFromNode;
+	}
+	// make sure we get called when the time is right
+	dojo.event.connect(dojo.hostenv, "loaded", tf);
+})();
+}catch(e){ alert(e); }

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,239 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.TabPane");
+dojo.provide("dojo.widget.html.TabPane");
+dojo.provide("dojo.widget.Tab");
+
+dojo.require("dojo.lang.func");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+//////////////////////////////////////////
+// TabPane -- a set of Tabs
+//////////////////////////////////////////
+dojo.widget.html.TabPane = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.TabPane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TabPane, {
+	widgetType: "TabPane",
+    isContainer: true,
+
+	// Constructor arguments
+	labelPosition: "top",
+	closeButton: "none",
+
+	useVisibility: false,		// true-->use visibility:hidden instead of display:none
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTabPane.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTabPane.css"),
+
+	selectedTab: "",		// initially selected tab (widgetId)
+
+	fillInTemplate: function(args, frag) {
+		// Copy style info and id from input node to output node
+		var source = this.getFragNodeRef(frag);
+		this.domNode.style.cssText = source.style.cssText;
+		dojo.html.addClass(this.domNode, dojo.html.getClass(source));
+		dojo.widget.html.TabPane.superclass.fillInTemplate.call(this, args, frag);
+	},
+
+	postCreate: function(args, frag) {
+		// Load all the tabs, creating a label for each one
+		for(var i=0; i<this.children.length; i++){
+			this._setupTab(this.children[i]);
+		}
+
+		if (this.closeButton=="pane") {
+			var div = document.createElement("div");
+			dojo.html.addClass(div, "dojoTabPanePaneClose");
+			var self = this;
+			dojo.event.connect(div, "onclick", function(){ self._runOnCloseTab(self.selectedTabWidget); });
+			dojo.event.connect(div, "onmouseover", function(){ dojo.html.addClass(div, "dojoTabPanePaneCloseHover"); });
+			dojo.event.connect(div, "onmouseout", function(){ dojo.html.removeClass(div, "dojoTabPanePaneCloseHover"); });
+			this.dojoTabLabels.appendChild(div);
+		}
+
+		dojo.html.addClass(this.dojoTabLabels, "dojoTabLabels-"+this.labelPosition);
+
+        this._setPadding();
+
+		// Display the selected tab
+		if(this.selectedTabWidget){
+			this.selectTab(this.selectedTabWidget);
+		}
+	},
+
+	addChild: function(child, overrideContainerNode, pos, ref, insertIndex){
+		this._setupTab(child);
+		dojo.widget.html.TabPane.superclass.addChild.call(this,child, overrideContainerNode, pos, ref, insertIndex);
+
+		// in case the tab labels have overflowed from one line to two lines
+		this._setPadding();
+	},
+
+	_setupTab: function(tab){
+		tab.domNode.style.display="none";
+
+		// Create label
+		tab.div = document.createElement("div");
+		dojo.html.addClass(tab.div, "dojoTabPaneTab");
+		var span = document.createElement("span");
+		span.innerHTML = tab.label;
+		dojo.html.disableSelection(span);
+		if (this.closeButton=="tab") {
+			var img = document.createElement("div");
+			dojo.html.addClass(img, "dojoTabPaneTabClose");
+			var self = this;
+			dojo.event.connect(img, "onclick", function(){ self._runOnCloseTab(tab); });
+			dojo.event.connect(img, "onmouseover", function(){ dojo.html.addClass(img,"dojoTabPaneTabCloseHover"); });
+			dojo.event.connect(img, "onmouseout", function(){ dojo.html.removeClass(img,"dojoTabPaneTabCloseHover"); });
+			span.appendChild(img);
+		}
+		tab.div.appendChild(span);
+		this.dojoTabLabels.appendChild(tab.div);
+		
+		var self = this;
+		dojo.event.connect(tab.div, "onclick", function(){ self.selectTab(tab); });
+		
+		if(!this.selectedTabWidget || this.selectedTab==tab.widgetId || tab.selected){
+    		this.selectedTabWidget = tab;
+        } else {
+            this._hideTab(tab);
+        }
+	},
+
+	// Configure the content pane to take up all the space except for where the tab labels are
+	_setPadding: function(){
+		var t=dojo.style.getOuterHeight(this.dojoTabLabels);
+		var w=dojo.style.getInnerWidth(this.dojoTabLabels);
+		var c=dojo.style.getOuterHeight(this.containerNode);
+		if(t==0 || c==0){
+			// browser needs more time to compute sizes (maybe CSS hasn't downloaded yet)
+			dojo.lang.setTimeout(this, this._setPadding, 10);
+			return;
+		}
+		with(this.domNode.style){
+			switch (this.labelPosition) {
+				case 'top': 
+					paddingTop=t+"px";
+					break;
+				case 'bottom':
+					paddingBottom=t+"px";
+					break;
+				case 'left':
+					paddingLeft=w+"px";
+					break;
+				case 'right':
+					paddingRight=w+"px";
+					break;
+				case 'left-h':
+					paddingLeft=w+"px";
+					break;
+				case 'right-h':
+					paddingRight=w+"px";
+					break;
+			}
+		}
+	},
+
+    removeChild: function(tab) {
+
+		// remove tab event handlers
+		dojo.event.disconnect(tab.div, "onclick", function () { });
+		if (this.closeButton=="tab") {
+			var img = tab.div.lastChild.lastChild;
+			if (img) {
+				dojo.html.removeClass(img, "dojoTabPaneTabClose", function () { });
+				dojo.event.disconnect(img, "onclick", function () { });
+				dojo.event.disconnect(img, "onmouseover", function () { });
+				dojo.event.disconnect(img, "onmouseout", function () { });
+			}
+		}
+
+        dojo.widget.html.TabPane.superclass.removeChild.call(this, tab);
+
+        dojo.html.removeClass(tab.domNode, "dojoTabPanel");
+        this.dojoTabLabels.removeChild(tab.div);
+        delete(tab.div);
+
+        if (this.selectedTabWidget === tab) {
+            this.selectedTabWidget = undefined;
+            if (this.children.length > 0) {
+                this.selectTab(this.children[0]);
+            }
+        }
+
+		// in case the tab labels have overflowed from one line to two lines
+		this._setPadding();
+    },
+
+    selectTab: function(tab) {
+		// Deselect old tab and select new one
+		if (this.selectedTabWidget) {
+			this._hideTab(this.selectedTabWidget);
+		}
+		this.selectedTabWidget = tab;
+		this._showTab(tab);
+		dojo.widget.html.TabPane.superclass.onResized.call(this);
+	},
+	
+	_showTab: function(tab) {
+		dojo.html.addClass(tab.div, "current");
+		tab.selected=true;
+		if ( this.useVisibility && !dojo.render.html.ie ) {
+			tab.domNode.style.visibility="visible";
+		} else {
+			tab.show();
+		}
+	},
+
+	_hideTab: function(tab) {
+		dojo.html.removeClass(tab.div, "current");
+		tab.selected=false;
+		if( this.useVisibility ){
+			tab.domNode.style.visibility="hidden";
+		}else{
+			tab.hide();
+		}
+	},
+
+	_runOnCloseTab: function(tab) {
+		var onc = tab.extraArgs.onClose || tab.extraArgs.onclose;
+		var fcn = dojo.lang.isFunction(onc) ? onc : window[onc];
+		if(dojo.lang.isFunction(fcn)) {
+			if(fcn(this,tab)) {
+				this.removeChild(tab);
+			}
+		} else
+			this.removeChild(tab);
+	},
+
+	onResized: function() {
+		// in case the tab labels have overflowed from one line to two lines
+		this._setPadding();
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:TabPane");
+
+// These arguments can be specified for the children of a TabPane.
+// Since any widget can be specified as a TabPane child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	label: "",
+	selected: false	// is this tab currently selected?
+});
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,279 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Tabs");
+dojo.provide("dojo.widget.html.Tabs");
+
+dojo.deprecated("dojo.widget.Tabs",  "use dojo.widget.TabPane", "0.3");
+
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:tabs");
+
+dojo.widget.html.Tabs = function() {
+	dojo.widget.HtmlWidget.call(this);
+	this.tabs = [];
+	this.panels = [];
+}
+dojo.inherits(dojo.widget.html.Tabs, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.Tabs, {
+
+	widgetType: "Tabs",
+	isContainer: true,
+
+	templatePath: null, // prolly not
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTabs.css"),
+
+	domNode: null,
+	containerNode: null,
+
+	selected: -1,
+
+	tabTarget: "",
+	extractContent: false, // find the bits inside <body>
+	parseContent: false, // parse externally loaded pages for widgets
+
+	preventCache: false,
+
+	buildRendering: function(args, frag) {
+		dojo.style.insertCssFile(this.templateCssPath);
+		this.domNode = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+		if(!this.domNode) { dj_error("html.Tabs: No node reference"); }
+
+		if(args["tabtarget"]) {
+			this.tabtarget = args["tabtarget"];
+			this.containerNode = document.getElementById(args["tabtarget"]);
+		} else {
+			this.containerNode = document.createElement("div");
+			var next = this.domNode.nextSibling;
+			if(next) {
+				this.domNode.parentNode.insertBefore(this.containerNode, next);
+			} else {
+				this.domNode.parentNode.appendChild(this.containerNode);
+			}
+		}
+		dojo.html.addClass(this.containerNode, "dojoTabPanelContainer");
+
+		var li = dojo.dom.getFirstChildElement(this.domNode);
+		while(li) {
+			var a = li.getElementsByTagName("a").item(0);
+			this.addTab(a);
+			li = dojo.dom.getNextSiblingElement(li);
+		}
+
+		if(this.selected == -1) { this.selected = 0; }
+		this.selectTab(null, this.tabs[this.selected]);
+	},
+
+	addTab: function(title, url, tabId, tabHandler) {
+		// TODO: make this an object proper
+		var panel = {
+			url: null,
+			title: null,
+			isLoaded: false,
+			id: null,
+			isLocal: false
+		};
+
+		function isLocal(a) {
+			var url = a.getAttribute("href");
+			var hash = url.indexOf("#");
+			if(hash == 0) {
+				return true;
+			}
+			var loc = location.href.split("#")[0];
+			var url2 = url.split("#")[0];
+			if(loc == url2) {
+				return true;
+			}
+			if(unescape(loc) == url2) {
+				return true;
+			}
+			var outer = a.outerHTML;
+			if(outer && /href=["']?#/i.test(outer)) {
+				return true;
+			}
+			return false;
+		}
+
+		if(title && title.tagName && title.tagName.toLowerCase() == "a") {
+			// init case
+			var a = title;
+			var li = a.parentNode;
+			title = a.innerHTML;
+			url = a.getAttribute("href");
+			var id = null;
+			var hash = url.indexOf("#");
+			if(isLocal(a)) {
+				id = url.split("#")[1];
+				dj_debug("setting local id:", id);
+				url = "#" + id;
+				panel.isLocal = true;
+			} else {
+				id = a.getAttribute("tabid");
+			}
+
+			panel.url = url;
+			panel.title = title;
+			panel.id = id || dojo.html.getUniqueId();
+			dj_debug("panel id:", panel.id, "url:", panel.url);
+		} else {
+			// programmatically adding
+			var li = document.createElement("li");
+			var a = document.createElement("a");
+			a.innerHTML = title;
+			a.href = url;
+			li.appendChild(a);
+			this.domNode.appendChild(li);
+
+			panel.url = url;
+			panel.title = title;
+			panel.id = tabId || dojo.html.getUniqueId();
+			dj_debug("prg tab:", panel.id, "url:", panel.url);
+		}
+
+		if(panel.isLocal) {
+			var node = document.getElementById(id);
+			node.style.display = "none";
+			this.containerNode.appendChild(node);
+		} else {
+			var node = document.createElement("div");
+			node.style.display = "none";
+			node.id = panel.id;
+			this.containerNode.appendChild(node);
+		}
+
+		var handler = a.getAttribute("tabhandler") || tabHandler;
+		if(handler) {
+			this.setPanelHandler(handler, panel);
+		}
+
+		dojo.event.connect(a, "onclick", this, "selectTab");
+
+		this.tabs.push(li);
+		this.panels.push(panel);
+
+		if(this.selected == -1 && dojo.html.hasClass(li, "current")) {
+			this.selected = this.tabs.length-1;
+		}
+
+		return { "tab": li, "panel": panel };
+	},
+
+	selectTab: function(e, target) {
+		if(dojo.lang.isNumber(e)) {
+			target = this.tabs[e];
+		}
+		else if(e) {
+			if(e.target) {
+				target = e.target;
+				while(target && (target.tagName||"").toLowerCase() != "li") {
+					target = target.parentNode;
+				}
+			}
+			if(e.preventDefault) { e.preventDefault(); }
+		}
+
+		dojo.html.removeClass(this.tabs[this.selected], "current");
+
+		for(var i = 0; i < this.tabs.length; i++) {
+			if(this.tabs[i] == target) {
+				dojo.html.addClass(this.tabs[i], "current");
+				this.selected = i;
+				break;
+			}
+		}
+
+		var panel = this.panels[this.selected];
+		if(panel) {
+			this.getPanel(panel);
+			this.hidePanels(panel);
+			document.getElementById(panel.id).style.display = "";
+		}
+	},
+
+	setPanelHandler: function(handler, panel) {
+		var fcn = dojo.lang.isFunction(handler) ? handler : window[handler];
+		if(!dojo.lang.isFunction(fcn)) {
+			throw new Error("Unable to set panel handler, '" + handler + "' not a function.");
+			return;
+		}
+		this["tabHandler" + panel.id] = function() {
+			return fcn.apply(this, arguments);
+		}
+	},
+
+	runPanelHandler: function(panel) {
+		if(dojo.lang.isFunction(this["tabHandler" + panel.id])) {
+			this["tabHandler" + panel.id](panel, document.getElementById(panel.id));
+			return false;
+		}
+		return true;
+	},
+
+	getPanel: function(panel) {
+		if(this.runPanelHandler(panel)) {
+			if(panel.isLocal) {
+				// do nothing?
+			} else {
+				if(!panel.isLoaded || !this.useCache) {
+					this.setExternalContent(panel, panel.url, this.useCache, this.preventCache);
+				}
+			}
+		}
+	},
+
+	setExternalContent: function(panel, url, useCache, preventCache) {
+		var node = document.getElementById(panel.id);
+		node.innerHTML = "Loading...";
+
+		var extract = this.extractContent;
+		var parse = this.parseContent;
+
+		dojo.io.bind({
+			url: url,
+			useCache: useCache,
+			preventCache: preventCache,
+			mimetype: "text/html",
+			handler: function(type, data, e) {
+				if(type == "load") {
+					if(extract) {
+						var matches = data.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+						if(matches) { data = matches[1]; }
+					}
+					node.innerHTML = data;
+					panel.isLoaded = true;
+					if(parse) {
+						var parser = new dojo.xml.Parse();
+						var frag = parser.parseElement(node, null, true);
+						dojo.widget.getParser().createComponents(frag);
+					}
+				} else {
+					node.innerHTML = "Error loading '" + panel.url + "' (" + e.status + " " + e.statusText + ")";
+				}
+			}
+		});
+	},
+
+	hidePanels: function(except) {
+		for(var i = 0; i < this.panels.length; i++) {
+			if(this.panels[i] != except && this.panels[i].id) {
+				var p = document.getElementById(this.panels[i].id);
+				if(p) {
+					p.style.display = "none";
+				}
+			}
+		}
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,32 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.TaskBar");
+dojo.provide("dojo.widget.TaskBarItem");
+dojo.require("dojo.widget.Widget");
+
+dojo.widget.TaskBar = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "TaskBar";
+	this.isContainer = true;
+}
+dojo.inherits(dojo.widget.TaskBar, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:taskbar");
+
+dojo.widget.TaskBarItem = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "TaskBarItem";
+}
+dojo.inherits(dojo.widget.TaskBarItem, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:taskbaritem");
+
+dojo.requireAfterIf("html", "dojo.widget.html.TaskBar");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,113 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.TimePicker");
+dojo.provide("dojo.widget.TimePicker.util");
+dojo.require("dojo.widget.DomWidget");
+
+dojo.widget.TimePicker = function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "TimePicker";
+	this.isContainer = false;
+	// the following aliases prevent breaking people using 0.2.x
+	this.toRfcDateTime = dojo.widget.TimePicker.util.toRfcDateTime;
+	this.fromRfcDateTime = dojo.widget.TimePicker.util.fromRfcDateTime;
+	this.toAmPmHour = dojo.widget.TimePicker.util.toAmPmHour;
+	this.fromAmPmHour = dojo.widget.TimePicker.util.fromAmPmHour;
+}
+
+dojo.inherits(dojo.widget.TimePicker, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:timepicker");
+
+dojo.requireAfterIf("html", "dojo.widget.html.TimePicker");
+
+dojo.widget.TimePicker.util = new function() {
+	// utility functions
+	this.toRfcDateTime = function(jsDate) {
+		if(!jsDate) {
+			jsDate = new Date();
+		}
+		var year = jsDate.getFullYear();
+		var month = jsDate.getMonth() + 1;
+		if (month < 10) {
+			month = "0" + month.toString();
+		}
+		var date = jsDate.getDate();
+		if (date < 10) {
+			date = "0" + date.toString();
+		}
+		var hour = jsDate.getHours();
+		if (hour < 10) {
+			hour = "0" + hour.toString();
+		}
+		var minute = jsDate.getMinutes();
+		if (minute < 10) {
+			minute = "0" + minute.toString();
+		}
+		// no way to set seconds, so set to zero
+		var second = "00";
+		var timeZone = jsDate.getTimezoneOffset();
+		var timeZoneHour = parseInt(timeZone/60);
+		if(timeZoneHour > -10 && timeZoneHour < 0) {
+			timeZoneHour = "-0" + Math.abs(timeZoneHour);
+		} else if(timeZoneHour < 10) {
+			timeZoneHour = "+0" + timeZoneHour.toString();
+		} else if(timeZoneHour >= 10) {
+			timeZoneHour = "+" + timeZoneHour.toString();
+		}
+		var timeZoneMinute = timeZone%60;
+		if(timeZoneMinute < 10) {
+			timeZoneMinute = "0" + timeZoneMinute.toString();
+		}
+		return year + "-" + month + "-" + date + "T" + hour + ":" + minute + ":" + second + timeZoneHour +":" + timeZoneMinute;
+	}
+
+	this.fromRfcDateTime = function(rfcDate, useDefaultMinutes) {
+		var tempDate = new Date();
+		if(!rfcDate || !rfcDate.split("T")[1]) {
+			if(useDefaultMinutes) {
+				tempDate.setMinutes(Math.floor(tempDate.getMinutes()/5)*5);
+			} else {
+				tempDate.setMinutes(0);
+			}
+		} else {
+			var tempTime = rfcDate.split("T")[1].split(":");
+			// fullYear, month, date
+			var tempDate = new Date();
+			tempDate.setHours(tempTime[0]);
+			tempDate.setMinutes(tempTime[1]);
+		}
+		return tempDate;
+	}
+
+	this.toAmPmHour = function(hour) {
+		var amPmHour = hour;
+		var isAm = true;
+		if (amPmHour == 0) {
+			amPmHour = 12;
+		} else if (amPmHour>12) {
+			amPmHour = amPmHour - 12;
+			isAm = false;
+		} else if (amPmHour == 12) {
+			isAm = false;
+		}
+		return [amPmHour, isAm];
+	}
+
+	this.fromAmPmHour = function(amPmHour, isAm) {
+		var hour = parseInt(amPmHour, 10);
+		if(isAm && hour == 12) {
+			hour = 0;
+		} else if (!isAm && hour<12) {
+			hour = hour + 12;
+		}
+		return hour;
+	}
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toggler.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toggler.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toggler.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toggler.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,40 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Toggler");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+
+// clicking on this node shows/hides another widget
+
+dojo.widget.Toggler = function(){
+	dojo.widget.DomWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.Toggler, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.Toggler, {
+	widgetType: "Toggler",
+	
+	// Associated widget 
+	targetId: '',
+	
+	fillInTemplate: function() {
+		dojo.event.connect(this.domNode, "onclick", this, "onClick");
+	},
+	
+	onClick: function() {
+		var pane = dojo.widget.getWidgetById(this.targetId);
+		if ( !pane || !pane.toggle ) { return; }
+		pane.explodeSrc = this.domNode;
+		pane.doToggle();
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:toggler");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toolbar.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toolbar.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toolbar.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Toolbar.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,970 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.ToolbarContainer");
+dojo.provide("dojo.widget.html.ToolbarContainer");
+dojo.provide("dojo.widget.Toolbar");
+dojo.provide("dojo.widget.html.Toolbar");
+dojo.provide("dojo.widget.ToolbarItem");
+dojo.provide("dojo.widget.html.ToolbarButtonGroup");
+dojo.provide("dojo.widget.html.ToolbarButton");
+dojo.provide("dojo.widget.html.ToolbarDialog");
+dojo.provide("dojo.widget.html.ToolbarMenu");
+dojo.provide("dojo.widget.html.ToolbarSeparator");
+dojo.provide("dojo.widget.html.ToolbarSpace");
+dojo.provide("dojo.widget.Icon");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html");
+
+/* ToolbarContainer
+ *******************/
+dojo.widget.html.ToolbarContainer = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.widgetType = "ToolbarContainer";
+	this.isContainer = true;
+
+	this.templateString = '<div class="toolbarContainer" dojoAttachPoint="containerNode"></div>';
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlToolbar.css");
+
+	this.getItem = function(name) {
+		if(name instanceof dojo.widget.ToolbarItem) { return name; }
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				var item = child.getItem(name);
+				if(item) { return item; }
+			}
+		}
+		return null;
+	}
+
+	this.getItems = function() {
+		var items = [];
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				items = items.concat(child.getItems());
+			}
+		}
+		return items;
+	}
+
+	this.enable = function() {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.enable.apply(child, arguments);
+			}
+		}
+	}
+
+	this.disable = function() {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.disable.apply(child, arguments);
+			}
+		}
+	}
+
+	this.select = function(name) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.select(arguments);
+			}
+		}
+	}
+
+	this.deselect = function(name) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				child.deselect(arguments);
+			}
+		}
+	}
+
+	this.getItemsState = function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				dojo.lang.mixin(values, child.getItemsState());
+			}
+		}
+		return values;
+	}
+
+	this.getItemsActiveState = function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				dojo.lang.mixin(values, child.getItemsActiveState());
+			}
+		}
+		return values;
+	}
+
+	this.getItemsSelectedState = function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.html.Toolbar) {
+				dojo.lang.mixin(values, child.getItemsSelectedState());
+			}
+		}
+		return values;
+	}
+}
+dojo.inherits(dojo.widget.html.ToolbarContainer, dojo.widget.HtmlWidget);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarContainer");
+
+/* Toolbar
+ **********/
+dojo.widget.html.Toolbar = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.widgetType = "Toolbar";
+	this.isContainer = true;
+
+	this.templateString = '<div class="toolbar" dojoAttachPoint="containerNode" unselectable="on" dojoOnMouseover="_onmouseover" dojoOnMouseout="_onmouseout" dojoOnClick="_onclick" dojoOnMousedown="_onmousedown" dojoOnMouseup="_onmouseup"></div>';
+	//this.templateString = '<div class="toolbar" dojoAttachPoint="containerNode" unselectable="on"></div>';
+
+	// given a node, tries to find it's toolbar item
+	this._getItem = function(node) {
+		var start = new Date();
+		var widget = null;
+		while(node && node != this.domNode) {
+			if(dojo.html.hasClass(node, "toolbarItem")) {
+				var widgets = dojo.widget.manager.getWidgetsByFilter(function(w) { return w.domNode == node; });
+				if(widgets.length == 1) {
+					widget = widgets[0];
+					break;
+				} else if(widgets.length > 1) {
+					dojo.raise("Toolbar._getItem: More than one widget matches the node");
+				}
+			}
+			node = node.parentNode;
+		}
+		return widget;
+	}
+
+	this._onmouseover = function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmouseover) { widget._onmouseover(e); }
+	}
+
+	this._onmouseout = function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmouseout) { widget._onmouseout(e); }
+	}
+
+	this._onclick = function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onclick){ 
+			widget._onclick(e);
+		}
+	}
+
+	this._onmousedown = function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmousedown) { widget._onmousedown(e); }
+	}
+
+	this._onmouseup = function(e) {
+		var widget = this._getItem(e.target);
+		if(widget && widget._onmouseup) { widget._onmouseup(e); }
+	}
+
+	var oldAddChild = this.addChild;
+	this.addChild = function(item, pos, props) {
+		var widget = dojo.widget.ToolbarItem.make(item, null, props);
+		var ret = oldAddChild.call(this, widget, null, pos, null);
+		return ret;
+	}
+
+	this.push = function() {
+		for(var i = 0; i < arguments.length; i++) {
+			this.addChild(arguments[i]);
+		}
+	}
+
+	this.getItem = function(name) {
+		if(name instanceof dojo.widget.ToolbarItem) { return name; }
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem
+				&& child._name == name) { return child; }
+		}
+		return null;
+	}
+
+	this.getItems = function() {
+		var items = [];
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				items.push(child);
+			}
+		}
+		return items;
+	}
+
+	this.getItemsState = function() {
+		var values = {};
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				values[child._name] = {
+					selected: child._selected,
+					enabled: child._enabled
+				};
+			}
+		}
+		return values;
+	}
+
+	this.getItemsActiveState = function() {
+		var values = this.getItemsState();
+		for(var item in values) {
+			values[item] = values[item].enabled;
+		}
+		return values;
+	}
+
+	this.getItemsSelectedState = function() {
+		var values = this.getItemsState();
+		for(var item in values) {
+			values[item] = values[item].selected;
+		}
+		return values;
+	}
+
+	this.enable = function() {
+		var items = arguments.length ? arguments : this.children;
+		for(var i = 0; i < items.length; i++) {
+			var child = this.getItem(items[i]);
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.enable(false, true);
+			}
+		}
+	}
+
+	this.disable = function() {
+		var items = arguments.length ? arguments : this.children;
+		for(var i = 0; i < items.length; i++) {
+			var child = this.getItem(items[i]);
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.disable();
+			}
+		}
+	}
+
+	this.select = function() {
+		for(var i = 0; i < arguments.length; i++) {
+			var name = arguments[i];
+			var item = this.getItem(name);
+			if(item) { item.select(); }
+		}
+	}
+
+	this.deselect = function() {
+		for(var i = 0; i < arguments.length; i++) {
+			var name = arguments[i];
+			var item = this.getItem(name);
+			if(item) { item.disable(); }
+		}
+	}
+
+	this.setValue = function() {
+		for(var i = 0; i < arguments.length; i += 2) {
+			var name = arguments[i], value = arguments[i+1];
+			var item = this.getItem(name);
+			if(item) {
+				if(item instanceof dojo.widget.ToolbarItem) {
+					item.setValue(value);
+				}
+			}
+		}
+	}
+}
+dojo.inherits(dojo.widget.html.Toolbar, dojo.widget.HtmlWidget);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbar");
+
+/* ToolbarItem hierarchy:
+	- ToolbarItem
+		- ToolbarButton
+		- ToolbarDialog
+			- ToolbarMenu
+		- ToolbarSeparator
+			- ToolbarSpace
+				- ToolbarFlexibleSpace
+*/
+
+
+/* ToolbarItem
+ **************/
+dojo.widget.ToolbarItem = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.ToolbarItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.ToolbarItem, {
+	templateString: '<span unselectable="on" class="toolbarItem"></span>',
+
+	_name: null,
+	getName: function() { return this._name; },
+	setName: function(value) { return this._name = value; },
+	getValue: function() { return this.getName(); },
+	setValue: function(value) { return this.setName(value); },
+
+	_selected: false,
+	isSelected: function() { return this._selected; },
+	setSelected: function(is, force, preventEvent) {
+		if(!this._toggleItem && !force) { return; }
+		is = Boolean(is);
+		if(force || this._enabled && this._selected != is) {
+			this._selected = is;
+			this.update();
+			if(!preventEvent) {
+				this._fireEvent(is ? "onSelect" : "onDeselect");
+				this._fireEvent("onChangeSelect");
+			}
+		}
+	},
+	select: function(force, preventEvent) {
+		return this.setSelected(true, force, preventEvent);
+	},
+	deselect: function(force, preventEvent) {
+		return this.setSelected(false, force, preventEvent);
+	},
+
+	_toggleItem: false,
+	isToggleItem: function() { return this._toggleItem; },
+	setToggleItem: function(value) { this._toggleItem = Boolean(value); },
+
+	toggleSelected: function(force) {
+		return this.setSelected(!this._selected, force);
+	},
+
+	_enabled: true,
+	isEnabled: function() { return this._enabled; },
+	setEnabled: function(is, force, preventEvent) {
+		is = Boolean(is);
+		if(force || this._enabled != is) {
+			this._enabled = is;
+			this.update();
+			if(!preventEvent) {
+				this._fireEvent(this._enabled ? "onEnable" : "onDisable");
+				this._fireEvent("onChangeEnabled");
+			}
+		}
+		return this._enabled;
+	},
+	enable: function(force, preventEvent) {
+		return this.setEnabled(true, force, preventEvent);
+	},
+	disable: function(force, preventEvent) {
+		return this.setEnabled(false, force, preventEvent);
+	},
+	toggleEnabled: function(force, preventEvent) {
+		return this.setEnabled(!this._enabled, force, preventEvent);
+	},
+
+	_icon: null,
+	getIcon: function() { return this._icon; },
+	setIcon: function(value) {
+		var icon = dojo.widget.Icon.make(value);
+		if(this._icon) {
+			this._icon.setIcon(icon);
+		} else {
+			this._icon = icon;
+		}
+		var iconNode = this._icon.getNode();
+		if(iconNode.parentNode != this.domNode) {
+			if(this.domNode.hasChildNodes()) {
+				this.domNode.insertBefore(iconNode, this.domNode.firstChild);
+			} else {
+				this.domNode.appendChild(iconNode);
+			}
+		}
+		return this._icon;
+	},
+
+	// TODO: update the label node (this.labelNode?)
+	_label: "",
+	getLabel: function() { return this._label; },
+	setLabel: function(value) {
+		var ret = this._label = value;
+		if(!this.labelNode) {
+			this.labelNode = document.createElement("span");
+			this.domNode.appendChild(this.labelNode);
+		}
+		this.labelNode.innerHTML = "";
+		this.labelNode.appendChild(document.createTextNode(this._label));
+		this.update();
+		return ret;
+	},
+
+	// fired from: setSelected, setEnabled, setLabel
+	update: function() {
+		if(this._enabled) {
+			dojo.html.removeClass(this.domNode, "disabled");
+			if(this._selected) {
+				dojo.html.addClass(this.domNode, "selected");
+			} else {
+				dojo.html.removeClass(this.domNode, "selected");
+			}
+		} else {
+			this._selected = false;
+			dojo.html.addClass(this.domNode, "disabled");
+			dojo.html.removeClass(this.domNode, "down");
+			dojo.html.removeClass(this.domNode, "hover");
+		}
+		this._updateIcon();
+	},
+
+	_updateIcon: function() {
+		if(this._icon) {
+			if(this._enabled) {
+				if(this._cssHover) {
+					this._icon.hover();
+				} else if(this._selected) {
+					this._icon.select();
+				} else {
+					this._icon.enable();
+				}
+			} else {
+				this._icon.disable();
+			}
+		}
+	},
+
+	_fireEvent: function(evt) {
+		if(typeof this[evt] == "function") {
+			var args = [this];
+			for(var i = 1; i < arguments.length; i++) {
+				args.push(arguments[i]);
+			}
+			this[evt].apply(this, args);
+		}
+	},
+
+	_onmouseover: function(e) {
+		if(!this._enabled) { return };
+		dojo.html.addClass(this.domNode, "hover");
+	},
+
+	_onmouseout: function(e) {
+		dojo.html.removeClass(this.domNode, "hover");
+		dojo.html.removeClass(this.domNode, "down");
+		if(!this._selected) {
+			dojo.html.removeClass(this.domNode, "selected");
+		}
+	},
+
+	_onclick: function(e) {
+		// FIXME: buttons never seem to have this._enabled set to true on Opera 9
+		// dojo.debug("widget:", this.widgetType, ":", this.getName(), ", enabled:", this._enabled);
+		if(this._enabled && !this._toggleItem) {
+			this._fireEvent("onClick");
+		}
+	},
+
+	_onmousedown: function(e) {
+		if(e.preventDefault) { e.preventDefault(); }
+		if(!this._enabled) { return };
+		dojo.html.addClass(this.domNode, "down");
+		if(this._toggleItem) {
+			if(this.parent.preventDeselect && this._selected) {
+				return;
+			}
+			this.toggleSelected();
+		}
+	},
+
+	_onmouseup: function(e) {
+		dojo.html.removeClass(this.domNode, "down");
+	},
+
+	fillInTemplate: function(args, frag) {
+		if(args.name) { this._name = args.name; }
+		if(args.selected) { this.select(); }
+		if(args.disabled) { this.disable(); }
+		if(args.label) { this.setLabel(args.label); }
+		if(args.icon) { this.setIcon(args.icon); }
+		if(args.toggleitem||args.toggleItem) { this.setToggleItem(true); }
+	}
+});
+
+dojo.widget.ToolbarItem.make = function(wh, whIsType, props) {
+	var item = null;
+
+	if(wh instanceof Array) {
+		item = dojo.widget.createWidget("ToolbarButtonGroup", props);
+		item.setName(wh[0]);
+		for(var i = 1; i < wh.length; i++) {
+			item.addChild(wh[i]);
+		}
+	} else if(wh instanceof dojo.widget.ToolbarItem) {
+		item = wh;
+	} else if(wh instanceof dojo.uri.Uri) {
+		item = dojo.widget.createWidget("ToolbarButton",
+			dojo.lang.mixin(props||{}, {icon: new dojo.widget.Icon(wh.toString())}));
+	} else if(whIsType) {
+		item = dojo.widget.createWidget(wh, props)
+	} else if(typeof wh == "string" || wh instanceof String) {
+		switch(wh.charAt(0)) {
+			case "|":
+			case "-":
+			case "/":
+				item = dojo.widget.createWidget("ToolbarSeparator", props);
+				break;
+			case " ":
+				if(wh.length == 1) {
+					item = dojo.widget.createWidget("ToolbarSpace", props);
+				} else {
+					item = dojo.widget.createWidget("ToolbarFlexibleSpace", props);
+				}
+				break;
+			default:
+				if(/\.(gif|jpg|jpeg|png)$/i.test(wh)) {
+					item = dojo.widget.createWidget("ToolbarButton",
+						dojo.lang.mixin(props||{}, {icon: new dojo.widget.Icon(wh.toString())}));
+				} else {
+					item = dojo.widget.createWidget("ToolbarButton",
+						dojo.lang.mixin(props||{}, {label: wh.toString()}));
+				}
+		}
+	} else if(wh && wh.tagName && /^img$/i.test(wh.tagName)) {
+		item = dojo.widget.createWidget("ToolbarButton",
+			dojo.lang.mixin(props||{}, {icon: wh}));
+	} else {
+		item = dojo.widget.createWidget("ToolbarButton",
+			dojo.lang.mixin(props||{}, {label: wh.toString()}));
+	}
+	return item;
+}
+
+/* ToolbarButtonGroup
+ *********************/
+dojo.widget.html.ToolbarButtonGroup = function() {
+	dojo.widget.ToolbarItem.call(this);
+
+	this.widgetType = "ToolbarButtonGroup";
+	this.isContainer = true;
+
+	this.templateString = '<span unselectable="on" class="toolbarButtonGroup" dojoAttachPoint="containerNode"></span>';
+
+	// if a button has the same name, it will be selected
+	// if this is set to a number, the button at that index will be selected
+	this.defaultButton = "";
+
+    this.postCreate = function() {
+        for (var i = 0; i < this.children.length; i++) {
+            this._injectChild(this.children[i]);
+        }
+    }
+
+    var oldAddChild = this.addChild;
+	this.addChild = function(item, pos, props) {
+		var widget = dojo.widget.ToolbarItem.make(item, null, dojo.lang.mixin(props||{}, {toggleItem:true}));
+		var ret = oldAddChild.call(this, widget, null, pos, null);
+        this._injectChild(widget);
+        return ret;
+    }
+
+    this._injectChild = function(widget) {
+        dojo.event.connect(widget, "onSelect", this, "onChildSelected");
+        if(widget._name == this.defaultButton
+			|| (typeof this.defaultButton == "number"
+			&& this.children.length-1 == this.defaultButton)) {
+			widget.select(false, true);
+		}
+	}
+
+	this.getItem = function(name) {
+		if(name instanceof dojo.widget.ToolbarItem) { return name; }
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem
+				&& child._name == name) { return child; }
+		}
+		return null;
+	}
+
+	this.getItems = function() {
+		var items = [];
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				items.push(child);
+			}
+		}
+		return items;
+	}
+
+	this.onChildSelected = function(e) {
+		this.select(e._name);
+	}
+
+	this.enable = function(force, preventEvent) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.enable(force, preventEvent);
+				if(child._name == this._value) {
+					child.select(force, preventEvent);
+				}
+			}
+		}
+	}
+
+	this.disable = function(force, preventEvent) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				child.disable(force, preventEvent);
+			}
+		}
+	}
+
+	this._value = "";
+	this.getValue = function() { return this._value; }
+
+	this.select = function(name, force, preventEvent) {
+		for(var i = 0; i < this.children.length; i++) {
+			var child = this.children[i];
+			if(child instanceof dojo.widget.ToolbarItem) {
+				if(child._name == name) {
+					child.select(force, preventEvent);
+					this._value = name;
+				} else {
+					child.deselect(true, preventEvent);
+				}
+			}
+		}
+		if(!preventEvent) {
+			this._fireEvent("onSelect", this._value);
+			this._fireEvent("onChangeSelect", this._value);
+		}
+	}
+	this.setValue = this.select;
+
+	this.preventDeselect = false; // if true, once you select one, you can't have none selected
+}
+dojo.inherits(dojo.widget.html.ToolbarButtonGroup, dojo.widget.ToolbarItem);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarButtonGroup");
+
+/* ToolbarButton
+ ***********************/
+dojo.widget.html.ToolbarButton = function() {
+	dojo.widget.ToolbarItem.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarButton, dojo.widget.ToolbarItem);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarButton");
+
+dojo.lang.extend(dojo.widget.html.ToolbarButton, {
+	widgetType: "ToolbarButton",
+
+	fillInTemplate: function(args, frag) {
+		dojo.widget.html.ToolbarButton.superclass.fillInTemplate.call(this, args, frag);
+		dojo.html.addClass(this.domNode, "toolbarButton");
+		if(this._icon) {
+			this.setIcon(this._icon);
+		}
+		if(this._label) {
+			this.setLabel(this._label);
+		}
+
+		if(!this._name) {
+			if(this._label) {
+				this.setName(this._label);
+			} else if(this._icon) {
+				var src = this._icon.getSrc("enabled").match(/[\/^]([^\.\/]+)\.(gif|jpg|jpeg|png)$/i);
+				if(src) { this.setName(src[1]); }
+			} else {
+				this._name = this._widgetId;
+			}
+		}
+	}
+});
+
+/* ToolbarDialog
+ **********************/
+dojo.widget.html.ToolbarDialog = function() {
+	dojo.widget.html.ToolbarButton.call(this);
+}
+dojo.inherits(dojo.widget.html.ToolbarDialog, dojo.widget.html.ToolbarButton);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarDialog");
+
+dojo.lang.extend(dojo.widget.html.ToolbarDialog, {
+	widgetType: "ToolbarDialog",
+	
+	fillInTemplate: function (args, frag) {
+		dojo.widget.html.ToolbarDialog.superclass.fillInTemplate.call(this, args, frag);
+		dojo.event.connect(this, "onSelect", this, "showDialog");
+		dojo.event.connect(this, "onDeselect", this, "hideDialog");
+	},
+	
+	showDialog: function (e) {
+		dojo.lang.setTimeout(dojo.event.connect, 1, document, "onmousedown", this, "deselect");
+	},
+	
+	hideDialog: function (e) {
+		dojo.event.disconnect(document, "onmousedown", this, "deselect");
+	}
+
+});
+
+/* ToolbarMenu
+ **********************/
+dojo.widget.html.ToolbarMenu = function() {
+	dojo.widget.html.ToolbarDialog.call(this);
+
+	this.widgetType = "ToolbarMenu";
+}
+dojo.inherits(dojo.widget.html.ToolbarMenu, dojo.widget.html.ToolbarDialog);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarMenu");
+
+/* ToolbarMenuItem
+ ******************/
+dojo.widget.ToolbarMenuItem = function() {
+}
+
+/* ToolbarSeparator
+ **********************/
+dojo.widget.html.ToolbarSeparator = function() {
+	dojo.widget.ToolbarItem.call(this);
+
+	this.widgetType = "ToolbarSeparator";
+	this.templateString = '<span unselectable="on" class="toolbarItem toolbarSeparator"></span>';
+
+	this.defaultIconPath = new dojo.uri.dojoUri("src/widget/templates/buttons/-.gif");
+
+	var oldFillInTemplate = this.fillInTemplate;
+	this.fillInTemplate = function(args, frag, skip) {
+		oldFillInTemplate.call(this, args, frag);
+		this._name = this.widgetId;
+		if(!skip) {
+			if(!this._icon) {
+				this.setIcon(this.defaultIconPath);
+			}
+			this.domNode.appendChild(this._icon.getNode());
+		}
+	}
+
+	// don't want events!
+	this._onmouseover = this._onmouseout = this._onclick
+		= this._onmousedown = this._onmouseup = null;
+}
+dojo.inherits(dojo.widget.html.ToolbarSeparator, dojo.widget.ToolbarItem);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSeparator");
+
+/* ToolbarSpace
+ **********************/
+dojo.widget.html.ToolbarSpace = function() {
+	dojo.widget.html.ToolbarSeparator.call(this);
+
+	this.widgetType = "ToolbarSpace";
+
+	var oldFillInTemplate = this.fillInTemplate;
+	this.fillInTemplate = function(args, frag, skip) {
+		oldFillInTemplate.call(this, args, frag, true);
+		if(!skip) {
+			dojo.html.addClass(this.domNode, "toolbarSpace");
+		}
+	}
+}
+dojo.inherits(dojo.widget.html.ToolbarSpace, dojo.widget.html.ToolbarSeparator);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSpace");
+
+/* ToolbarSelect
+ ******************/
+ 
+/*dojo.widget.html.ToolbarSelect = function() {
+	dojo.widget.html.ToolbarDialog.call(this);
+
+	// fix inheritence chain
+	for (var method in this.constructor.prototype) {
+		this[method] = this.constructor.prototype[method];
+	}
+}
+dojo.inherits(dojo.widget.html.ToolbarSelect, dojo.widget.html.ToolbarDialog);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSelect");
+
+dojo.lang.extend(dojo.widget.html.ToolbarSelect, {
+	widgetType: "ToolbarSelect",
+	
+	fillInTemplate: function (args, frag) {
+		dojo.widget.html.ToolbarSelect.superclass.fillInTemplate.call(this, args, frag);
+		
+		this.dialog = document.createElement("ul");
+		for(var value in args.values) {
+			var li = document.createElement("li");
+			li.value = args.values[value];
+			li.appendChild(document.createTextNode(value));
+			this.dialog.appendChild(li);
+		}
+	},
+	
+	showDialog: function (e) {
+		dojo.widget.html.ToolbarSelect.superclass.showDialog.call(this, e);
+		with (dojo.html) {
+			var x = getAbsoluteX(this.domNode);
+			var y = getAbsoluteY(this.domNode) + getInnerHeight(this.domNode);
+		}
+		with (this.domNode.style) { top = y + "px"; left = x + "px"; }
+		document.body.appendChild(this.dialog);
+	},
+	
+	hideDialog: function (e) {
+		dojo.widget.html.ToolbarSelect.superclass.hideDialog.call(this, e);
+		this.dialog.parentNode.removeChild(this.dialog);
+	}
+
+});*/
+
+dojo.widget.html.ToolbarSelect = function() {
+	dojo.widget.ToolbarItem.call(this);
+	this.widgetType = "ToolbarSelect";
+	this.templateString = '<span class="toolbarItem toolbarSelect" unselectable="on"><select dojoAttachPoint="selectBox" dojoOnChange="changed"></select></span>';
+
+	var oldFillInTemplate = this.fillInTemplate;
+	this.fillInTemplate = function(args, frag) {
+		oldFillInTemplate.call(this, args, frag, true);
+		var keys = args.values;
+		var i = 0;
+		for(var val in keys) {
+			var opt = document.createElement("option");
+			opt.setAttribute("value", keys[val]);
+			opt.innerHTML = val;
+			this.selectBox.appendChild(opt);
+		}
+	}
+
+	this.changed = function(e) {
+		this._fireEvent("onSetValue", this.selectBox.value);
+	}
+
+	var oldSetEnabled = this.setEnabled;
+	this.setEnabled = function(is, force, preventEvent) {
+		var ret = oldSetEnabled.call(this, is, force, preventEvent);
+		this.selectBox.disabled = !this._enabled;
+		return ret;
+	}
+
+	// don't want events!
+	this._onmouseover = this._onmouseout = this._onclick
+		= this._onmousedown = this._onmouseup = null;
+}
+dojo.inherits(dojo.widget.html.ToolbarSelect, dojo.widget.ToolbarItem);
+dojo.widget.tags.addParseTreeHandler("dojo:toolbarSelect");
+
+/* Icon
+ *********/
+// arguments can be IMG nodes, Image() instances or URLs -- enabled is the only one required
+dojo.widget.Icon = function(enabled, disabled, hover, selected) {
+	if(arguments.length == 0) {
+		throw new Error("Icon must have at least an enabled state");
+	}
+	var states = ["enabled", "disabled", "hover", "selected"];
+	var currentState = "enabled";
+	var domNode = document.createElement("img");
+
+	this.getState = function() { return currentState; }
+	this.setState = function(value) {
+		if(dojo.lang.inArray(value, states)) {
+			if(this[value]) {
+				currentState = value;
+				domNode.setAttribute("src", this[currentState].src);
+			}
+		} else {
+			throw new Error("Invalid state set on Icon (state: " + value + ")");
+		}
+	}
+
+	this.setSrc = function(state, value) {
+		if(/^img$/i.test(value.tagName)) {
+			this[state] = value;
+		} else if(typeof value == "string" || value instanceof String
+			|| value instanceof dojo.uri.Uri) {
+			this[state] = new Image();
+			this[state].src = value.toString();
+		}
+		return this[state];
+	}
+
+	this.setIcon = function(icon) {
+		for(var i = 0; i < states.length; i++) {
+			if(icon[states[i]]) {
+				this.setSrc(states[i], icon[states[i]]);
+			}
+		}
+		this.update();
+	}
+
+	this.enable = function() { this.setState("enabled"); }
+	this.disable = function() { this.setState("disabled"); }
+	this.hover = function() { this.setState("hover"); }
+	this.select = function() { this.setState("selected"); }
+
+	this.getSize = function() {
+		return {
+			width: domNode.width||domNode.offsetWidth,
+			height: domNode.height||domNode.offsetHeight
+		};
+	}
+
+	this.setSize = function(w, h) {
+		domNode.width = w;
+		domNode.height = h;
+		return { width: w, height: h };
+	}
+
+	this.getNode = function() {
+		return domNode;
+	}
+
+	this.getSrc = function(state) {
+		if(state) { return this[state].src; }
+		return domNode.src||"";
+	}
+
+	this.update = function() {
+		this.setState(currentState);
+	}
+
+	for(var i = 0; i < states.length; i++) {
+		var arg = arguments[i];
+		var state = states[i];
+		this[state] = null;
+		if(!arg) { continue; }
+		this.setSrc(state, arg);
+	}
+
+	this.enable();
+}
+
+dojo.widget.Icon.make = function(a,b,c,d) {
+	for(var i = 0; i < arguments.length; i++) {
+		if(arguments[i] instanceof dojo.widget.Icon) {
+			return arguments[i];
+		} else if(!arguments[i]) {
+			nullArgs++;
+		}
+	}
+
+	return new dojo.widget.Icon(a,b,c,d);
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tooltip.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tooltip.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tooltip.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tooltip.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,24 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.Tooltip");
+dojo.require("dojo.widget.Widget");
+
+dojo.widget.tags.addParseTreeHandler("dojo:tooltip");
+
+dojo.widget.Tooltip = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "Tooltip";
+	this.isContainer = true;
+}
+dojo.inherits(dojo.widget.Tooltip, dojo.widget.Widget);
+
+dojo.requireAfterIf("html", "dojo.widget.html.Tooltip");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tree.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tree.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tree.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tree.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,514 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+/**
+ * Tree model does all the drawing, visual node management etc.
+ * Throws events about clicks on it, so someone may catch them and process
+ * Tree knows nothing about DnD stuff, covered in TreeDragAndDrop and (if enabled) attached by controller
+*/
+dojo.provide("dojo.widget.Tree");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.TreeNode");
+
+
+
+// make it a tag
+dojo.widget.tags.addParseTreeHandler("dojo:Tree");
+
+
+dojo.widget.Tree = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.eventNames = {};
+
+	this.tree = this;
+	this.DNDAcceptTypes = [];
+	this.actionsDisabled = [];
+
+}
+dojo.inherits(dojo.widget.Tree, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.Tree, {
+	widgetType: "Tree",
+
+	eventNamesDefault: {
+		// new child does not get domNode filled in (only template draft)
+		// until addChild->createDOMNode is called(program way) OR createDOMNode (html-way)
+		// hook events to operate on new DOMNode, create dropTargets etc
+		createDOMNode: "createDOMNode",
+		// tree created.. Perform tree-wide actions if needed
+		treeCreate: "treeCreate",
+		// expand icon clicked
+		treeClick: "treeClick",
+		// node icon clicked
+		iconClick: "iconClick",
+		// node title clicked
+		titleClick: "titleClick",
+
+		moveFrom: "moveFrom",
+		moveTo: "moveTo",
+		addChild: "addChild",
+		removeChild: "removeChild",
+		expand: "expand",
+		collapse: "collapse"
+	},
+
+	isContainer: true,
+
+	DNDMode: "off",
+
+	DNDModes: {
+		BETWEEN: 1,
+		ONTO: 2
+	},
+
+	DNDAcceptTypes: "",
+
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/images/Tree/Tree.css"),
+
+	templateString: '<div class="dojoTree"></div>',
+
+	isExpanded: true, // consider this "root node" to be always expanded
+
+	isTree: true,
+
+	objectId: "",
+
+	// autoCreate if not "off"
+	// used to get the autocreated controller ONLY.
+	// generally, tree DOES NOT KNOW about its CONTROLLER, it just doesn't care
+	// controller gets messages via dojo.event
+	controller: "",
+
+	// autoCreate if not "off"
+	// used to get the autocreated selector ONLY.
+	// generally, tree DOES NOT KNOW its SELECTOR
+	// binding is made with dojo.event
+	selector: "",
+
+	// used ONLY at initialization time
+	menu: "", // autobind menu if menu's widgetId is set here
+
+	expandLevel: "", // expand to level automatically
+
+	//
+	// these icons control the grid and expando buttons for the whole tree
+	//
+
+	blankIconSrc: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_blank.gif"),
+
+	gridIconSrcT: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_t.gif"), // for non-last child grid
+	gridIconSrcL: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_l.gif"), // for last child grid
+	gridIconSrcV: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_v.gif"), // vertical line
+	gridIconSrcP: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_p.gif"), // for under parent item child icons
+	gridIconSrcC: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_c.gif"), // for under child item child icons
+	gridIconSrcX: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_x.gif"), // grid for sole root item
+	gridIconSrcY: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_y.gif"), // grid for last rrot item
+	gridIconSrcZ: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_grid_z.gif"), // for under root parent item child icon
+
+	expandIconSrcPlus: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_expand_plus.gif"),
+	expandIconSrcMinus: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_expand_minus.gif"),
+	expandIconSrcLoading: dojo.uri.dojoUri("src/widget/templates/images/Tree/treenode_loading.gif"),
+
+
+	iconWidth: 18,
+	iconHeight: 18,
+
+
+	//
+	// tree options
+	//
+
+	showGrid: true,
+	showRootGrid: true,
+
+	actionIsDisabled: function(action) {
+		var _this = this;
+		return dojo.lang.inArray(_this.actionsDisabled, action)
+	},
+
+
+	actions: {
+    	ADDCHILD: "ADDCHILD"
+	},
+
+
+	getInfo: function() {
+		var info = {
+			widgetId: this.widgetId,
+			objectId: this.objectId
+		}
+
+		return info;
+	},
+
+	initializeController: function() {
+		if (this.controller != "off") {
+			if (this.controller) {
+				this.controller = dojo.widget.byId(this.controller);
+			}
+			else {
+				// create default controller here
+				dojo.require("dojo.widget.TreeBasicController");
+				this.controller = dojo.widget.createWidget("TreeBasicController",
+					{ DNDController: this.DNDMode ? "create" : "" }
+				 );
+
+			}
+			this.controller.listenTree(this); // controller listens to my events
+
+		} else {
+			this.controller = null;
+		}
+	},
+
+	initializeSelector: function() {
+
+		if (this.selector != "off") {
+			if (this.selector) {
+				this.selector = dojo.widget.byId(this.selector);
+			}
+			else {
+				// create default controller here
+				dojo.require("dojo.widget.TreeSelector");
+				this.selector = dojo.widget.createWidget("TreeSelector");
+			}
+
+			this.selector.listenTree(this);
+		} else {
+			this.selector = null;
+		}
+	},
+
+	initialize: function(args, frag){
+
+		var _this = this;
+
+		for(name in this.eventNamesDefault) {
+			if (dojo.lang.isUndefined(this.eventNames[name])) {
+				this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name];
+			}
+		}
+
+		for(var i=0; i<this.actionsDisabled.length; i++) {
+			this.actionsDisabled[i] = this.actionsDisabled[i].toUpperCase();
+		}
+
+		if (this.DNDMode == "off") {
+			this.DNDMode = 0;
+		} else if (this.DNDMode == "between") {
+			this.DNDMode = this.DNDModes.ONTO | this.DNDModes.BETWEEN;
+		} else if (this.DNDMode == "onto") {
+			this.DNDMode = this.DNDModes.ONTO;
+		}
+
+		this.expandLevel = parseInt(this.expandLevel);
+
+		this.initializeController();
+		this.initializeSelector();
+
+		if (this.menu) {
+			this.menu = dojo.widget.byId(this.menu);
+			this.menu.listenTree(this);
+		}
+
+
+		this.containerNode = this.domNode;
+
+	},
+
+
+	postCreate: function() {
+		this.createDOMNode();
+	},
+
+
+	createDOMNode: function() {
+
+		dojo.html.disableSelection(this.domNode);
+
+		for(var i=0; i<this.children.length; i++){
+			this.children[i].parent = this; // root nodes have tree as parent
+
+			var node = this.children[i].createDOMNode(this, 0);
+
+
+			this.domNode.appendChild(node);
+		}
+
+
+		if (!this.showRootGrid){
+			for(var i=0; i<this.children.length; i++){
+				this.children[i].expand();
+			}
+		}
+
+		dojo.event.topic.publish(this.tree.eventNames.treeCreate, { source: this } );
+
+	},
+
+
+	addChild: function(child, index) {
+		var message = {
+			child: child,
+			index: index,
+			parent: this,
+			// remember if dom was already initialized
+			// initialized => no createDOMNode => no createDOMNode event
+			domNodeInitialized: child.domNodeInitialized
+		}
+
+		this.doAddChild.apply(this, arguments);
+
+		dojo.event.topic.publish(this.tree.eventNames.addChild, message);
+	},
+
+
+	// not called for initial tree building. See createDOMNode instead.
+	// builds child html node if needed
+	// index is "last node" by default
+	/**
+	 * FIXME: Is it possible that removeNode from the tree will cause leaks cause of attached events ?
+	 * if yes, then only attach events in addChild and detach in remove.. Seems all ok yet.
+	*/
+	doAddChild: function(child, index){
+
+		//dojo.debug("doAddChild "+index+" called for "+child);
+
+
+		if (dojo.lang.isUndefined(index)) {
+			index = this.children.length;
+		}
+
+		if (!child.isTreeNode){
+			dojo.raise("You can only add TreeNode widgets to a "+this.widgetType+" widget!");
+			return;
+		}
+
+		// usually it is impossible to change "isFolder" state, but if anyone wants to add a child to leaf,
+		// it is possible program-way.
+		if (this.isTreeNode){
+			if (!this.isFolder) { // just became a folder.
+				//dojo.debug("becoming folder "+this);
+				this.setFolder();
+			}
+		}
+
+		// adjust tree
+		var _this = this;
+		dojo.lang.forEach(child.getDescendants(), function(elem) { elem.tree = _this.tree; });
+
+		// fix parent
+		child.parent = this;
+
+
+		// no dynamic loading for those who become parents
+		if (this.isTreeNode) {
+			this.state = this.loadStates.LOADED;
+		}
+
+
+		// add new child into DOM after it was added into children
+		if (index < this.children.length) { // children[] already has child
+			//dojo.debug("Inserting before "+this.children[index].title);
+			dojo.dom.insertBefore(child.domNode, this.children[index].domNode);
+		} else {
+			this.containerNode.appendChild(child.domNode);
+			if (this.isExpanded && this.isTreeNode) {
+				/* When I add children to hidden containerNode => show container w/ them */
+				this.showChildren();
+			}
+		}
+
+
+		this.children.splice(index, 0, child);
+
+		//dojo.debugShallow(this.children);
+
+
+		// if node exists - adjust its depth, otherwise build it
+		if (child.domNodeInitialized) {
+			var d = this.isTreeNode ? this.depth : -1;
+			child.adjustDepth( d - child.depth + 1 );
+
+
+			// update icons to link generated dom with Tree => updateParentGrid
+			// if I moved child from LastNode inside the tree => need to link it up'n'down =>
+			// updateExpandGridColumn
+			// if I change depth => need to update all grid..
+			child.updateIconTree();
+		} else {
+			//dojo.debug("Create domnode ");
+			child.depth = this.isTreeNode ? this.depth+1 : 0;
+			child.createDOMNode(child.tree, child.depth);
+		}
+
+
+		// Use-case:
+		// When previous sibling was created => it was last, no children after it
+		// so it did not create link down => let's add it for all descendants
+		// Use-case:
+		// a child was moved down under the last node so last node should be updated
+		var prevSibling = child.getPreviousSibling();
+		if (child.isLastNode() && prevSibling) {
+			prevSibling.updateExpandGridColumn();
+		}
+
+
+		//dojo.debug("Added child "+child);
+
+
+
+	},
+
+
+
+
+	makeBlankImg: function() {
+		var img = document.createElement('img');
+
+		img.style.width = this.iconWidth + 'px';
+		img.style.height = this.iconHeight + 'px';
+		img.src = this.blankIconSrc;
+		img.style.verticalAlign = 'middle';
+
+		return img;
+	},
+
+
+	updateIconTree: function(){
+
+		//dojo.debug("Update icons for "+this)
+		if (!this.isTree) {
+			this.updateIcons();
+		}
+
+		for(var i=0; i<this.children.length; i++){
+			this.children[i].updateIconTree();
+		}
+
+	},
+
+	toString: function() {
+		return "["+this.widgetType+" ID:"+this.widgetId+"]"
+	},
+
+
+
+
+	/**
+	 * Move child to newParent as last child
+	 * redraw tree and update icons.
+	 *
+	 * Called by target, saves source in event.
+	 * events are published for BOTH trees AFTER update.
+	*/
+	move: function(child, newParent, index) {
+
+		//dojo.debug(child+" "+newParent+" at "+index);
+
+		var oldParent = child.parent;
+		var oldTree = child.tree;
+
+		this.doMove.apply(this, arguments);
+
+		var newParent = child.parent;
+		var newTree = child.tree;
+
+		var message = {
+				oldParent: oldParent, oldTree: oldTree,
+				newParent: newParent, newTree: newTree,
+				child: child
+		};
+
+		/* publish events here about structural changes for both source and target trees */
+		dojo.event.topic.publish(oldTree.eventNames.moveFrom, message);
+		dojo.event.topic.publish(newTree.eventNames.moveTo, message);
+
+	},
+
+
+	/* do actual parent change here. Write remove child first */
+	doMove: function(child, newParent, index) {
+		//var parent = child.parent;
+		child.parent.doRemoveChild(child);
+
+		newParent.doAddChild(child, index);
+	},
+
+
+
+// ================================ removeChild ===================================
+
+	removeChild: function(child) {
+		if (!child.parent) return;
+
+		var oldTree = child.tree;
+		var oldParent = child.parent;
+
+		var removedChild = this.doRemoveChild.apply(this, arguments);
+
+
+		dojo.event.topic.publish(this.tree.eventNames.removeChild,
+			{ child: removedChild, tree: oldTree, parent: oldParent }
+		);
+
+		return removedChild;
+	},
+
+
+	doRemoveChild: function(child) {
+		if (!child.parent) return;
+
+		var parent = child.parent;
+
+		var children = parent.children;
+
+
+		var index = child.getParentIndex();
+		if (index < 0) {
+			dojo.raise("Couldn't find node "+child+" for removal");
+		}
+
+
+		children.splice(index,1);
+		dojo.dom.removeNode(child.domNode);
+
+		// if WAS last node (children.length decreased already) and has prevSibling
+		if (index == children.length && index>0) {
+			children[index-1].updateExpandGridColumn();
+		}
+
+		//parent.updateIconTree();
+
+
+		child.parent = child.tree = null;
+
+		return child;
+	},
+
+// ================================ destroyChild ===================================
+
+
+	destroyChild: function(child) {
+		dojo.debug("destroyed");
+		//dojo.debugShallow(child);
+		child.parent.removeChild(child);
+		child.cleanUp();
+		delete child;
+	}
+
+
+});
+
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeBasicController.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeBasicController.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeBasicController.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeBasicController.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,275 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+
+dojo.provide("dojo.widget.TreeBasicController");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.json")
+dojo.require("dojo.io.*");
+
+
+dojo.widget.tags.addParseTreeHandler("dojo:TreeBasicController");
+
+
+dojo.widget.TreeBasicController = function() {
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.TreeBasicController, dojo.widget.HtmlWidget);
+
+
+dojo.lang.extend(dojo.widget.TreeBasicController, {
+	widgetType: "TreeBasicController",
+
+	DNDController: "",
+
+
+	initialize: function(args, frag){
+
+		/* no DND by default for compatibility */
+		if (this.DNDController == "create") {
+			dojo.require("dojo.dnd.TreeDragAndDrop");
+			this.DNDController = new dojo.dnd.TreeDNDController(this);
+		}
+		else if (this.DNDController) {
+			this.DNDController = dojo.widget.byId(this.DNDController);
+		}
+
+
+	},
+
+
+	/**
+	 * Binds controller to all tree events
+	*/
+	listenTree: function(tree) {
+		//dojo.debug("Event "+tree.eventNames.treeClick);
+		dojo.event.topic.subscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode");
+		dojo.event.topic.subscribe(tree.eventNames.treeClick, this, "onTreeClick");
+		dojo.event.topic.subscribe(tree.eventNames.treeCreate, this, "onTreeCreate");
+
+		if (this.DNDController) {
+			this.DNDController.listenTree(tree);
+		}
+	},
+
+	onCreateDOMNode: function(message) {
+		
+		var node = message.source;
+				
+				
+		if (node.expandLevel > 0) {
+			//dojo.debug(node.expandLevel);
+			this.expandToLevel(node, node.expandLevel);
+		}		
+	},
+	
+	// perform actions-initializers for tree
+	onTreeCreate: function(message) {
+		var tree = message.source;
+		var _this = this;
+		if (tree.expandLevel) {
+			dojo.lang.forEach(tree.children,
+				function(child) { _this.expandToLevel(child, tree.expandLevel-1) }
+			);
+		}
+	},
+	
+	expandToLevel: function(node, level) {				
+		if (level == 0) return;
+		
+		var children = node.children;
+		var _this = this;
+		
+		var handler = function(node, expandLevel) {
+			this.node = node; 
+			this.expandLevel = expandLevel; 
+			// recursively expand opened node
+			this.process = function() {
+				//dojo.debug("Process "+node+" level "+level);
+				for(var i=0; i<this.node.children.length; i++) {
+					var child = node.children[i];					
+					
+					_this.expandToLevel(child, this.expandLevel);
+				}
+			};
+		}
+		
+		var h = new handler(node, level-1);					
+				
+				
+		this.expand(node, false, h, h.process);
+			
+	},
+		
+		
+		
+
+	onTreeClick: function(message){
+		var node = message.source;
+
+		//dojo.debug("Subscribed to "+node);
+
+		if (node.isExpanded){
+			this.collapse(node);
+		} else {
+			this.expand(node);
+		}
+	},
+
+	expand: function(node, sync, callObj, callFunc) {
+		//dojo.debug(node);
+		node.expand();
+		if (callFunc) callFunc.apply(callObj, [node]);
+	},
+
+	collapse: function(node) {
+		node.collapse();
+	},
+
+// =============================== move ============================
+
+	/**
+	 * Checks whether it is ok to change parent of child to newParent
+	 * May incur type checks etc
+	 *
+	 * It should check only hierarchical possibility w/o index, etc
+	 * because in onDragOver event for Between DND mode we can't calculate index at once on onDragOVer.
+	 * index changes as client moves mouse up-down over the node
+	 */
+	canMove: function(child, newParent){
+
+		if (child.actionIsDisabled(child.actions.MOVE)) {
+			return false;
+		}
+
+		if (child.parent !== newParent && newParent.actionIsDisabled(newParent.actions.ADDCHILD)) {
+			return false;
+		}
+
+		// Can't move parent under child. check whether new parent is child of "child".
+		var node = newParent;
+		while(node.isTreeNode) {
+			//dojo.debugShallow(node.title)
+			if (node === child) {
+				// parent of newParent is child
+				return false;
+			}
+			node = node.parent;
+		}
+
+
+		// check for newParent being a folder (if node)
+		if (newParent.isTreeNode && !newParent.isFolder) {
+			return false;
+		}
+
+		return true;
+	},
+
+
+	move: function(child, newParent, index) {
+
+		/* move sourceTreeNode to new parent */
+		if (!this.canMove(child, newParent)) {
+			return false;
+		}
+
+		var result = this.doMove(child, newParent, index);
+
+		if (!result) return result;
+
+		if (newParent.isTreeNode) {
+			this.expand(newParent);
+		}
+
+		return result;
+	},
+
+	doMove: function(child, newParent, index) {
+		child.tree.move(child, newParent, index);
+
+		return true;
+	},
+
+// =============================== removeChild ============================
+
+
+	canRemoveChild: function(child) {
+		if (child.actionIsDisabled(child.actions.REMOVE)) {
+			return false;
+		}
+
+		return true;
+	},
+
+
+	removeChild: function(node, callFunc, callObj) {
+		if (!this.canRemoveChild(node)) {
+			return false;
+		}
+
+		return this.doRemoveChild(node, callFunc, callObj);
+	},
+
+
+	doRemoveChild: function(node, callFunc, callObj) {
+		node.tree.removeChild(node);
+
+		if (callFunc) {
+			callFunc.apply(dojo.lang.isUndefined(callObj) ? this : callObj, [node]);
+		}
+	},
+
+
+	// -----------------------------------------------------------------------------
+	//                             Create node stuff
+	// -----------------------------------------------------------------------------
+
+
+	canCreateChild: function(parent, index, data) {
+		if (!parent.isFolder) return false;
+
+		return true;
+	},
+
+
+	/* send data to server and add child from server */
+	/* data may contain an almost ready child, or anything else, suggested to server */
+	/*in RPC controllers server responds with child data to be inserted */
+	createChild: function(parent, index, data, callFunc, callObj) {
+		if (!this.canCreateChild(parent, index, data)) {
+			return false;
+		}
+
+		return this.doCreateChild.apply(this, arguments);
+	},
+
+	doCreateChild: function(parent, index, data, callFunc, callObj) {
+
+		var widgetType = data.widgetType ? data.widgetType : "TreeNode";
+
+		var newChild = dojo.widget.createWidget(widgetType, data);
+
+		parent.addChild(newChild, index);
+
+		this.expand(parent);
+
+		if (callFunc) {
+			callFunc.apply(callObj, [newChild]);
+		}
+
+		return newChild;
+	}
+
+
+
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeContextMenu.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeContextMenu.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeContextMenu.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TreeContextMenu.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,191 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+
+
+dojo.provide("dojo.widget.TreeContextMenu");
+dojo.provide("dojo.widget.TreeMenuItem");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.Menu2");
+
+
+dojo.widget.tags.addParseTreeHandler("dojo:TreeContextMenu");
+dojo.widget.tags.addParseTreeHandler("dojo:TreeMenuItem");
+
+
+
+dojo.widget.TreeContextMenu = function() {
+	dojo.widget.PopupMenu2.call(this);
+
+	this.listenedTrees = [];
+
+}
+
+
+dojo.inherits(dojo.widget.TreeContextMenu, dojo.widget.PopupMenu2);
+
+dojo.lang.extend(dojo.widget.TreeContextMenu, {
+
+	widgetType: "TreeContextMenu",
+
+	open: function(x, y, parentMenu, explodeSrc){
+
+		var result = dojo.widget.PopupMenu2.prototype.open.apply(this, arguments);
+
+		/* publish many events here about structural changes */
+		dojo.event.topic.publish(this.eventNames.open, { menu:this });
+
+		return result;
+	},
+
+	listenTree: function(tree) {
+		/* add context menu to all nodes that exist already */
+		var nodes = tree.getDescendants();
+
+		for(var i=0; i<nodes.length; i++) {
+			if (!nodes[i].isTreeNode) continue;
+			this.bindDomNode(nodes[i].labelNode);
+		}
+
+
+		/* bind context menu to all nodes that will be created in the future (e.g loaded from server)*/
+		var _this = this;
+		dojo.event.topic.subscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode");
+		dojo.event.topic.subscribe(tree.eventNames.moveFrom, this, "onMoveFrom");
+		dojo.event.topic.subscribe(tree.eventNames.moveTo, this, "onMoveTo");
+		dojo.event.topic.subscribe(tree.eventNames.removeChild, this, "onRemoveChild");
+		dojo.event.topic.subscribe(tree.eventNames.addChild, this, "onAddChild");
+
+		this.listenedTrees.push(tree);
+
+	},
+
+	bindTreeNode: function(node) {
+		var _this = this;
+		//dojo.debug("bind to "+node);
+		dojo.lang.forEach(node.getDescendants(),
+			function(e) {_this.bindDomNode(e.labelNode); }
+		);
+	},
+
+
+	unBindTreeNode: function(node) {
+		var _this = this;
+		//dojo.debug("Unbind from "+node);
+		dojo.lang.forEach(node.getDescendants(),
+			function(e) {_this.unBindDomNode(e.labelNode); }
+		);
+	},
+
+	onCreateDOMNode: function(message) {
+		this.bindTreeNode(message.source);
+	},
+
+
+	onMoveFrom: function(message) {
+		if (!dojo.lang.inArray(this.listenedTrees, message.newTree)) {
+			this.unBindTreeNode(message.child);
+		}
+	},
+
+	onMoveTo: function(message) {
+		if (dojo.lang.inArray(this.listenedTrees, message.newTree)) {
+			this.bindTreeNode(message.child);
+		}
+	},
+
+	onRemoveChild: function(message) {
+		this.unBindTreeNode(message.child);
+	},
+
+	onAddChild: function(message) {
+		if (message.domNodeInitialized) {
+			// dom node was there already => I did not process onNodeDomCreate
+			this.bindTreeNode(message.child);
+		}
+	}
+
+
+});
+
+
+
+
+
+
+dojo.widget.TreeMenuItem = function() {
+	dojo.widget.MenuItem2.call(this);
+
+}
+
+
+dojo.inherits(dojo.widget.TreeMenuItem, dojo.widget.MenuItem2);
+
+
+dojo.lang.extend(dojo.widget.TreeMenuItem, {
+
+	widgetType: "TreeMenuItem",
+
+	// treeActions menu item performs following actions (to be checked for permissions)
+	treeActions: "",
+
+	initialize: function(args, frag) {
+
+		this.treeActions = this.treeActions.split(",");
+		for(var i=0; i<this.treeActions.length; i++) {
+			this.treeActions[i] = this.treeActions[i].toUpperCase();
+		}
+
+	},
+
+	getTreeNode: function() {
+		var menu = this;
+
+		while (! (menu instanceof dojo.widget.TreeContextMenu) ) {
+			menu = menu.parent;
+		}
+
+		var source = menu.getTopOpenEvent().target;
+
+		while (!source.getAttribute('treeNode') && source.tagName != 'body') {
+			source = source.parentNode;
+		}
+		if (source.tagName == 'body') {
+			dojo.raise("treeNode not detected");
+		}
+		var treeNode = dojo.widget.manager.getWidgetById(source.getAttribute('treeNode'));
+
+		return treeNode;
+	},
+
+
+	menuOpen: function(message) {
+		var treeNode = this.getTreeNode();
+
+		this.setDisabled(false); // enable by default
+
+		var _this = this;
+		dojo.lang.forEach(_this.treeActions,
+			function(action) {
+				_this.setDisabled( treeNode.actionIsDisabled(action) );
+			}
+		);
+
+	},
+
+	toString: function() {
+		return "["+this.widgetType+" node "+this.getTreeNode()+"]";
+	}
+
+});
+
+



---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org