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/05/23 01:11:13 UTC

svn commit: r408783 [22/27] - in /tapestry/tapestry4/trunk: examples/TimeTracker/src/context/ framework/src/descriptor/META-INF/ framework/src/java/org/apache/tapestry/ framework/src/java/org/apache/tapestry/dojo/ framework/src/java/org/apache/tapestry...

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeBasicController.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeBasicController.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeBasicController.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeBasicController.js Mon May 22 16:10:12 2006
@@ -0,0 +1,294 @@
+/*
+	Copyright (c) 2004-2006, 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: "",
+
+	dieWithTree: false,
+
+	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);
+		}
+
+
+
+	},
+
+
+	/**
+	 * 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");
+		dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+		if (this.DNDController) {
+			this.DNDController.listenTree(tree);
+		}
+	},
+
+	unlistenTree: function(tree) {
+		dojo.event.topic.unsubscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeClick, this, "onTreeClick");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeCreate, this, "onTreeCreate");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+	},
+
+	onTreeDestroy: function(message) {
+		var tree = message.source;
+
+		this.unlistenTree(tree);
+
+		if (this.dieWithTree) {
+			//alert("Killing myself "+this.widgetId);
+			this.destroy();
+			//dojo.debug("done");
+		}
+	},
+
+	onCreateDOMNode: function(message) {
+
+		var node = message.source;
+
+
+		if (node.expandLevel > 0) {
+			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;
+
+		if(node.isLocked()) {
+			return false;
+		}
+
+		if (node.isExpanded){
+			this.collapse(node);
+		} else {
+			this.expand(node);
+		}
+	},
+
+	expand: function(node, sync, callObj, callFunc) {
+		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 we move under same parent then no matter if ADDCHILD disabled for him
+		// but if we move to NEW parent then check if action is disabled for him
+		// also covers case for newParent being a non-folder in strict mode etc
+		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;
+		}
+
+		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;
+	},
+
+// =============================== removeNode ============================
+
+
+	canRemoveNode: function(child) {
+		if (child.actionIsDisabled(child.actions.REMOVE)) {
+			return false;
+		}
+
+		return true;
+	},
+
+
+	removeNode: function(node, callObj, callFunc) {
+		if (!this.canRemoveNode(node)) {
+			return false;
+		}
+
+		return this.doRemoveNode(node, callObj, callFunc);
+	},
+
+
+	doRemoveNode: function(node, callObj, callFunc) {
+		node.tree.removeNode(node);
+
+		if (callFunc) {
+			callFunc.apply(dojo.lang.isUndefined(callObj) ? this : callObj, [node]);
+		}
+	},
+
+
+	// -----------------------------------------------------------------------------
+	//                             Create node stuff
+	// -----------------------------------------------------------------------------
+
+
+	canCreateChild: function(parent, index, data) {
+		if (parent.actionIsDisabled(parent.actions.ADDCHILD)) 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, callObj, callFunc) {
+		if (!this.canCreateChild(parent, index, data)) {
+			return false;
+		}
+
+		return this.doCreateChild.apply(this, arguments);
+	},
+
+	doCreateChild: function(parent, index, data, callObj, callFunc) {
+
+		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: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeContextMenu.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeContextMenu.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeContextMenu.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeContextMenu.js Mon May 22 16:10:12 2006
@@ -0,0 +1,214 @@
+/*
+	Copyright (c) 2004-2006, 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.removeNode, this, "onRemoveNode");
+		dojo.event.topic.subscribe(tree.eventNames.addChild, this, "onAddChild");
+		dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+		this.listenedTrees.push(tree);
+
+	},
+
+	unlistenTree: function(tree) {
+		/* clear event listeners */
+
+		dojo.event.topic.unsubscribe(tree.eventNames.createDOMNode, this, "onCreateDOMNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.moveFrom, this, "onMoveFrom");
+		dojo.event.topic.unsubscribe(tree.eventNames.moveTo, this, "onMoveTo");
+		dojo.event.topic.unsubscribe(tree.eventNames.removeNode, this, "onRemoveNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.addChild, this, "onAddChild");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+		for(var i=0; i<this.listenedTrees.length; i++){
+           if(this.listenedTrees[i] === tree){
+                   this.listenedTrees.splice(i, 1);
+                   break;
+           }
+		}
+	},
+
+	onTreeDestroy: function(message) {
+		this.unlistenTree(message.source);
+	},
+
+	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);
+		}
+	},
+
+	onRemoveNode: 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()+"]";
+	}
+
+});
+
+

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeControllerExtension.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeControllerExtension.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeControllerExtension.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeControllerExtension.js Mon May 22 16:10:12 2006
@@ -0,0 +1,95 @@
+/*
+	Copyright (c) 2004-2006, 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
+*/
+
+/**
+ * Additional tree utils
+ *
+ */
+dojo.provide("dojo.widget.TreeControllerExtension");
+
+
+dojo.widget.TreeControllerExtension = function() { }
+
+dojo.lang.extend(dojo.widget.TreeControllerExtension, {
+
+	saveExpandedIndices: function(node, field) {
+		var obj = {};
+
+		for(var i=0; i<node.children.length; i++) {
+			if (node.children[i].isExpanded) {
+				var key = dojo.lang.isUndefined(field) ? i : node.children[i][field];
+				obj[key] = this.saveExpandedIndices(node.children[i], field);
+			}
+		}
+
+		return obj;
+	},
+
+
+	restoreExpandedIndices: function(node, savedIndices, field) {
+		var _this = this;
+
+		var handler = function(node, savedIndices) {
+			this.node = node; //.children[i];
+			this.savedIndices = savedIndices; //[i];
+			// recursively read next savedIndices level and apply to opened node
+			this.process = function() {
+				//dojo.debug("Callback applied for "+this.node);
+				_this.restoreExpandedIndices(this.node, this.savedIndices, field);
+			};
+		}
+
+
+		for(var i=0; i<node.children.length; i++) {
+			var child = node.children[i];
+
+			var found = false;
+			var key = -1;
+
+			//dojo.debug("Check "+child)
+			// process field set case
+			if (dojo.lang.isUndefined(field) && savedIndices[i]) {
+				found = true;
+				key = i;
+			}
+
+			// process case when field is not set
+			if (field) {
+				for(var key in savedIndices) {
+					//dojo.debug("Compare "+key+" "+child[field])
+					if (key == child[field]) {
+						found = true;
+						break;
+					}
+				}
+			}
+
+			// if we found anything - expand it
+			if (found) {
+				//dojo.debug("Found at "+key)
+				var h = new handler(child, savedIndices[key]);
+				_this.expand(child, false, h, h.process);
+			} else if (child.isExpanded) { // not found, so collapse
+				//dojo.debug("Collapsing all descendants "+node.children[i])
+				dojo.lang.forEach(child.getDescendants(), function(elem) { _this.collapse(elem); });
+				//this.collapse(node.children[i]);
+			}
+
+		}
+
+
+	}
+
+});
+
+
+
+
+

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeLoadingController.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeLoadingController.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeLoadingController.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeLoadingController.js Mon May 22 16:10:12 2006
@@ -0,0 +1,217 @@
+/*
+	Copyright (c) 2004-2006, 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.TreeLoadingController");
+
+dojo.require("dojo.widget.TreeBasicController");
+dojo.require("dojo.event.*");
+dojo.require("dojo.json")
+dojo.require("dojo.io.*");
+
+
+dojo.widget.tags.addParseTreeHandler("dojo:TreeLoadingController");
+
+
+dojo.widget.TreeLoadingController = function() {
+	dojo.widget.TreeBasicController.call(this);
+}
+
+dojo.inherits(dojo.widget.TreeLoadingController, dojo.widget.TreeBasicController);
+
+
+dojo.lang.extend(dojo.widget.TreeLoadingController, {
+	widgetType: "TreeLoadingController",
+
+	RPCUrl: "",
+
+	RPCActionParam: "action", // used for GET for RPCUrl
+
+
+	/**
+	 * Common RPC error handler (dies)
+	*/
+	RPCErrorHandler: function(type, obj, evt) {
+		alert( "RPC Error: " + (obj.message||"no message"));
+	},
+
+
+
+	getRPCUrl: function(action) {
+
+		// RPCUrl=local meant SOLELY for DEMO and LOCAL TESTS.
+		// May lead to widgetId collisions
+		if (this.RPCUrl == "local") {
+			var dir = document.location.href.substr(0, document.location.href.lastIndexOf('/'));
+			var localUrl = dir+"/"+action;
+			//dojo.debug(localUrl);
+			return localUrl;
+		}
+
+		if (!this.RPCUrl) {
+			dojo.raise("Empty RPCUrl: can't load");
+		}
+
+		return this.RPCUrl + ( this.RPCUrl.indexOf("?") > -1 ? "&" : "?") + this.RPCActionParam+"="+action;
+	},
+
+
+	/**
+	 * Add all loaded nodes from array obj as node children and expand it
+	*/
+	loadProcessResponse: function(node, result, callObj, callFunc) {
+
+		if (!dojo.lang.isUndefined(result.error)) {
+			this.RPCErrorHandler("server", result.error);
+			return false;
+		}
+
+		//dojo.debugShallow(result);
+
+		var newChildren = result;
+
+		if (!dojo.lang.isArray(newChildren)) {
+			dojo.raise('loadProcessResponse: Not array loaded: '+newChildren);
+		}
+
+		for(var i=0; i<newChildren.length; i++) {
+			// looks like dojo.widget.manager needs no special "add" command
+			newChildren[i] = dojo.widget.createWidget(node.widgetType, newChildren[i]);
+			node.addChild(newChildren[i]);
+		}
+
+
+		//node.addAllChildren(newChildren);
+
+		node.state = node.loadStates.LOADED;
+
+		//dojo.debug(callFunc);
+
+		if (dojo.lang.isFunction(callFunc)) {
+			callFunc.apply(dojo.lang.isUndefined(callObj) ? this : callObj, [node, newChildren]);
+		}
+		//this.expand(node);
+	},
+
+	getInfo: function(obj) {
+		return obj.getInfo();
+	},
+
+	runRPC: function(kw) {
+		var _this = this;
+
+		var handle = function(type, data, evt) {
+			// unlock BEFORE any processing is done
+			// so errorHandler may apply locking
+			if (kw.lock) {
+				dojo.lang.forEach(kw.lock,
+					function(t) { t.unlock() }
+				);
+			}
+
+			if(type == "load"){
+				kw.load.call(this, data);
+			}else{
+				this.RPCErrorHandler(type, data, evt);
+			}
+
+		}
+
+		if (kw.lock) {
+			dojo.lang.forEach(kw.lock,
+				function(t) { t.lock() }
+			);
+		}
+
+
+		dojo.io.bind({
+			url: kw.url,
+			/* I hitch to get this.loadOkHandler */
+			handle: dojo.lang.hitch(this, handle),
+			mimetype: "text/json",
+			preventCache: true,
+			sync: kw.sync,
+			content: { data: dojo.json.serialize(kw.params) }
+		});
+	},
+
+
+
+	/**
+	 * Load children of the node from server
+	 * Synchroneous loading doesn't break control flow
+	 * I need sync mode for DnD
+	*/
+	loadRemote: function(node, sync, callObj, callFunc){
+		var _this = this;
+
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree)
+		};
+
+		//dojo.debug(callFunc)
+
+		this.runRPC({
+			url: this.getRPCUrl('getChildren'),
+			load: function(result) {
+				_this.loadProcessResponse(node, result, callObj, callFunc) ;
+			},
+			sync: sync,
+			lock: [node],
+			params: params
+		});
+
+	},
+
+
+	expand: function(node, sync, callObj, callFunc) {
+
+		if (node.state == node.loadStates.UNCHECKED && node.isFolder) {
+
+			this.loadRemote(node, sync,
+				this,
+				function(node, newChildren) {
+					this.expand(node, sync, callObj, callFunc);
+				}
+			);
+
+			return;
+		}
+
+		dojo.widget.TreeBasicController.prototype.expand.apply(this, arguments);
+
+	},
+
+
+
+	doMove: function(child, newParent, index) {
+		/* load nodes into newParent in sync mode, if needed, first */
+		if (newParent.isTreeNode && newParent.state == newParent.loadStates.UNCHECKED) {
+			this.loadRemote(newParent, true);
+		}
+
+		return dojo.widget.TreeBasicController.prototype.doMove.apply(this, arguments);
+	},
+
+
+	doCreateChild: function(parent, index, data, callObj, callFunc) {
+
+		/* load nodes into newParent in sync mode, if needed, first */
+		if (parent.state == parent.loadStates.UNCHECKED) {
+			this.loadRemote(parent, true);
+		}
+
+		return dojo.widget.TreeBasicController.prototype.doCreateChild.apply(this, arguments);
+	}
+
+
+
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeNode.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeNode.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeNode.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeNode.js Mon May 22 16:10:12 2006
@@ -0,0 +1,536 @@
+/*
+	Copyright (c) 2004-2006, 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.TreeNode");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+
+// make it a tag
+dojo.widget.tags.addParseTreeHandler("dojo:TreeNode");
+
+
+// # //////////
+
+dojo.widget.TreeNode = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.actionsDisabled = [];
+}
+
+dojo.inherits(dojo.widget.TreeNode, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.TreeNode, {
+	widgetType: "TreeNode",
+
+	loadStates: {
+		UNCHECKED: "UNCHECKED",
+    	LOADING: "LOADING",
+    	LOADED: "LOADED"
+	},
+
+
+	actions: {
+		MOVE: "MOVE",
+    	REMOVE: "REMOVE",
+    	EDIT: "EDIT",
+    	ADDCHILD: "ADDCHILD"
+	},
+
+	isContainer: true,
+
+	lockLevel: 0, // lock ++ unlock --, so nested locking works fine
+
+
+	templateString: ('<div class="dojoTreeNode"> '
+		+ '<span treeNode="${this.widgetId}" class="dojoTreeNodeLabel" dojoAttachPoint="labelNode"> '
+		+ '		<span dojoAttachPoint="titleNode" dojoAttachEvent="onClick: onTitleClick" class="dojoTreeNodeLabelTitle">${this.title}</span> '
+		+ '</span> '
+		+ '<span class="dojoTreeNodeAfterLabel" dojoAttachPoint="afterLabelNode">${this.afterLabel}</span> '
+		+ '<div dojoAttachPoint="containerNode" style="display:none"></div> '
+		+ '</div>').replace(/(>|<)\s+/g, '$1'), // strip whitespaces between nodes
+
+
+	childIconSrc: "",
+	childIconFolderSrc: dojo.uri.dojoUri("src/widget/templates/images/Tree/closed.gif"), // for under root parent item child icon,
+	childIconDocumentSrc: dojo.uri.dojoUri("src/widget/templates/images/Tree/document.gif"), // for under root parent item child icon,
+
+	childIcon: null,
+	isTreeNode: true,
+
+	objectId: "", // the widget represents an object
+
+	afterLabel: "",
+	afterLabelNode: null, // node to the left of labelNode
+
+	// an icon left from childIcon: imgs[-2].
+	// if +/- for folders, blank for leaves
+	expandIcon: null,
+
+	title: "",
+	object: "", // node may have object attached, settable from HTML
+	isFolder: false,
+
+	labelNode: null, // the item label
+	titleNode: null, // the item title
+	imgs: null, // an array of icons imgs
+
+	expandLevel: "", // expand to level
+
+	tree: null,
+
+	depth: 0,
+
+	isExpanded: false,
+
+	state: null,  // after creation will change to loadStates: "loaded/loading/unchecked"
+	domNodeInitialized: false,  // domnode is initialized with icons etc
+
+
+	isFirstNode: function() {
+		return this.getParentIndex() == 0 ? true: false;
+	},
+
+	isLastNode: function() {
+		return this.getParentIndex() == this.parent.children.length-1 ? true : false;
+	},
+
+	lock: function(){ return this.tree.lock.apply(this, arguments) },
+	unlock: function(){ return this.tree.unlock.apply(this, arguments) },
+	isLocked: function(){ return this.tree.isLocked.apply(this, arguments) },
+	cleanLock: function(){ return this.tree.cleanLock.apply(this, arguments) },
+
+	actionIsDisabled: function(action) {
+		var _this = this;
+
+		var disabled = false;
+
+		if (this.tree.strictFolders && action == this.actions.ADDCHILD && !this.isFolder) {
+			disabled = true;
+		}
+
+		if (dojo.lang.inArray(_this.actionsDisabled, action)) {
+			disabled = true;
+		}
+
+		if (this.isLocked()) {
+			disabled = true;
+		}
+
+		return disabled;
+	},
+
+	getInfo: function() {
+		// No title here (title may be widget)
+		var info = {
+			widgetId: this.widgetId,
+			objectId: this.objectId,
+			index: this.getParentIndex(),
+			isFolder: this.isFolder
+		}
+
+		return info;
+	},
+
+	initialize: function(args, frag){
+
+		//dojo.debug(this.title)
+
+		this.state = this.loadStates.UNCHECKED;
+
+		for(var i=0; i<this.actionsDisabled.length; i++) {
+			this.actionsDisabled[i] = this.actionsDisabled[i].toUpperCase();
+		}
+
+		this.expandLevel = parseInt(this.expandLevel);
+
+	},
+
+
+	/**
+	 * Change visible node depth by appending/prepending with blankImgs
+	 * @param depthDiff Integer positive => move right, negative => move left
+	*/
+	adjustDepth: function(depthDiff) {
+
+		for(var i=0; i<this.children.length; i++) {
+			this.children[i].adjustDepth(depthDiff);
+		}
+
+		this.depth += depthDiff;
+
+		if (depthDiff>0) {
+			for(var i=0; i<depthDiff; i++) {
+				var img = this.tree.makeBlankImg();
+				this.imgs.unshift(img);
+				//dojo.debugShallow(this.domNode);
+				dojo.dom.insertBefore(this.imgs[0], this.domNode.firstChild);
+
+			}
+		}
+		if (depthDiff<0) {
+			for(var i=0; i<-depthDiff;i++) {
+				this.imgs.shift();
+				dojo.dom.removeNode(this.domNode.firstChild);
+			}
+		}
+
+	},
+
+
+	markLoading: function() {
+		this._markLoadingSavedIcon = this.expandIcon.src;
+		this.expandIcon.src = this.tree.expandIconSrcLoading;
+	},
+
+	// if icon is "Loading" then
+	unMarkLoading: function() {
+		if (!this._markLoadingSavedIcon) return;
+
+		var im = new Image();
+		im.src = this.tree.expandIconSrcLoading;
+
+		//dojo.debug("Unmark "+this.expandIcon.src+" : "+im.src);
+		if (this.expandIcon.src == im.src) {
+			this.expandIcon.src = this._markLoadingSavedIcon;
+		}
+		this._markLoadingSavedIcon = null;
+	},
+
+
+	setFolder: function() {
+		dojo.event.connect(this.expandIcon, 'onclick', this, 'onTreeClick');
+		this.expandIcon.src = this.isExpanded ? this.tree.expandIconSrcMinus : this.tree.expandIconSrcPlus;
+		this.isFolder = true;
+	},
+
+
+	createDOMNode: function(tree, depth){
+
+		this.tree = tree;
+		this.depth = depth;
+
+
+		//
+		// add the tree icons
+		//
+
+		this.imgs = [];
+
+		for(var i=0; i<this.depth+1; i++){
+
+			var img = this.tree.makeBlankImg();
+
+			this.domNode.insertBefore(img, this.labelNode);
+
+			this.imgs.push(img);
+		}
+
+
+		this.expandIcon = this.imgs[this.imgs.length-1];
+
+
+		this.childIcon = this.tree.makeBlankImg();
+
+		// add to images before the title
+		this.imgs.push(this.childIcon);
+
+		dojo.dom.insertBefore(this.childIcon, this.titleNode);
+
+		// node with children(from source html) becomes folder on build stage.
+		if (this.children.length || this.isFolder) {
+			this.setFolder();
+		}
+		else {
+			// leaves are always loaded
+			//dojo.debug("Set "+this+" state to loaded");
+			this.state = this.loadStates.LOADED;
+		}
+
+		dojo.event.connect(this.childIcon, 'onclick', this, 'onIconClick');
+
+
+		//
+		// create the child rows
+		//
+
+
+		for(var i=0; i<this.children.length; i++){
+			this.children[i].parent = this;
+
+			var node = this.children[i].createDOMNode(this.tree, this.depth+1);
+
+			this.containerNode.appendChild(node);
+		}
+
+
+		if (this.children.length) {
+			this.state = this.loadStates.LOADED;
+		}
+
+		this.updateIcons();
+
+		this.domNodeInitialized = true;
+
+		dojo.event.topic.publish(this.tree.eventNames.createDOMNode, { source: this } );
+
+		return this.domNode;
+	},
+
+	onTreeClick: function(e){
+		dojo.event.topic.publish(this.tree.eventNames.treeClick, { source: this, event: e });
+	},
+
+	onIconClick: function(e){
+		dojo.event.topic.publish(this.tree.eventNames.iconClick, { source: this, event: e });
+	},
+
+	onTitleClick: function(e){
+		dojo.event.topic.publish(this.tree.eventNames.titleClick, { source: this, event: e });
+	},
+
+	markSelected: function() {
+		dojo.html.addClass(this.titleNode, 'dojoTreeNodeLabelSelected');
+	},
+
+
+	unMarkSelected: function() {
+		//dojo.debug('unmark')
+		dojo.html.removeClass(this.titleNode, 'dojoTreeNodeLabelSelected');
+	},
+
+	updateExpandIcon: function() {
+		if (this.isFolder){
+			this.expandIcon.src = this.isExpanded ? this.tree.expandIconSrcMinus : this.tree.expandIconSrcPlus;
+		} else {
+			this.expandIcon.src = this.tree.blankIconSrc;
+		}
+	},
+
+	/* set the grid under the expand icon */
+	updateExpandGrid: function() {
+
+		if (this.tree.showGrid){
+			if (this.depth){
+				this.setGridImage(-2, this.isLastNode() ? this.tree.gridIconSrcL : this.tree.gridIconSrcT);
+			}else{
+				if (this.isFirstNode()){
+					this.setGridImage(-2, this.isLastNode() ? this.tree.gridIconSrcX : this.tree.gridIconSrcY);
+				}else{
+					this.setGridImage(-2, this.isLastNode() ? this.tree.gridIconSrcL : this.tree.gridIconSrcT);
+				}
+			}
+		}else{
+			this.setGridImage(-2, this.tree.blankIconSrc);
+		}
+
+	},
+
+	/* set the grid under the child icon */
+	updateChildGrid: function() {
+
+		if ((this.depth || this.tree.showRootGrid) && this.tree.showGrid){
+			this.setGridImage(-1, (this.children.length && this.isExpanded) ? this.tree.gridIconSrcP : this.tree.gridIconSrcC);
+		}else{
+			if (this.tree.showGrid && !this.tree.showRootGrid){
+				this.setGridImage(-1, (this.children.length && this.isExpanded) ? this.tree.gridIconSrcZ : this.tree.blankIconSrc);
+			}else{
+				this.setGridImage(-1, this.tree.blankIconSrc);
+			}
+		}
+
+
+	},
+
+	updateParentGrid: function() {
+		var parent = this.parent;
+
+		//dojo.debug("updateParentGrid "+this);
+
+		for(var i=0; i<this.depth; i++){
+
+			//dojo.debug("Parent "+parent);
+
+			var idx = this.imgs.length-(3+i);
+			var img = (this.tree.showGrid && !parent.isLastNode()) ? this.tree.gridIconSrcV : this.tree.blankIconSrc;
+
+			//dojo.debug("Image "+img+" for "+idx);
+
+			this.setGridImage(idx, img);
+
+			parent = parent.parent;
+		}
+	},
+
+	updateExpandGridColumn: function() {
+		if (!this.tree.showGrid) return;
+
+		var _this = this;
+
+		var icon = this.isLastNode() ? this.tree.blankIconSrc : this.tree.gridIconSrcV;
+
+		dojo.lang.forEach(_this.getDescendants(),
+			function(node) { node.setGridImage(_this.depth, icon); }
+		);
+
+		this.updateExpandGrid();
+	},
+
+	updateIcons: function(){
+
+
+		//dojo.profile.start("updateIcons")
+
+		//dojo.debug("Update icons for "+this)
+		//dojo.debug(this.isFolder)
+
+		this.imgs[0].style.display = this.tree.showRootGrid ? 'inline' : 'none';
+
+
+		//
+		// set the expand icon
+		//
+
+
+		//
+		// set the child icon
+		//
+		this.buildChildIcon();
+
+		this.updateExpandGrid();
+		this.updateChildGrid();
+		this.updateParentGrid();
+
+
+
+		dojo.profile.stop("updateIcons")
+
+	},
+
+	buildChildIcon: function() {
+		// IE (others?) tries to download whatever is on src attribute so setting "url()" like before isnt a good idea
+		// Only results in a 404
+		if(this.childIconSrc){
+			this.childIcon.src = this.childIconSrc;
+		}
+		this.childIcon.style.display = this.childIconSrc ? 'inline' : 'none';
+	},
+
+	setGridImage: function(idx, src){
+
+		if (idx < 0){
+			idx = this.imgs.length + idx;
+		}
+
+		//if (idx >= this.imgs.length-2) return;
+		this.imgs[idx].style.backgroundImage = 'url(' + src + ')';
+	},
+
+
+	updateIconTree: function(){
+		this.tree.updateIconTree.call(this);
+	},
+
+
+
+
+	expand: function(){
+		if (this.isExpanded) return;
+
+		if (this.children.length) {
+			this.showChildren();
+		}
+
+		this.isExpanded = true;
+
+		this.updateExpandIcon();
+
+		dojo.event.topic.publish(this.tree.eventNames.expand, {source: this} );
+	},
+
+	collapse: function(){
+		if (!this.isExpanded) return;
+
+		this.hideChildren();
+		this.isExpanded = false;
+
+		this.updateExpandIcon();
+
+		dojo.event.topic.publish(this.tree.eventNames.collapse, {source: this} );
+	},
+
+	hideChildren: function(){
+		var _this = this;
+		this.tree.toggleObj.hide(
+			_this.containerNode, _this.toggleDuration, _this.explodeSrc, _this.onHide
+		);
+
+		/* if dnd is in action, recalculate changed coordinates */
+		if(dojo.exists(dojo, 'dnd.dragManager.dragObjects') && dojo.dnd.dragManager.dragObjects.length) {
+			dojo.dnd.dragManager.cacheTargetLocations();
+		}
+	},
+
+	showChildren: function(){
+		var _this = this;
+		this.tree.toggleObj.show(
+			_this.containerNode, _this.toggleDuration,	_this.explodeSrc, _this.onShow
+		);
+
+		/* if dnd is in action, recalculate changed coordinates */
+		if(dojo.exists(dojo, 'dnd.dragManager.dragObjects') && dojo.dnd.dragManager.dragObjects.length) {
+			dojo.dnd.dragManager.cacheTargetLocations();
+		}
+	},
+
+	addChild: function(){
+		return this.tree.addChild.apply(this, arguments);
+	},
+
+	doAddChild: function(){
+		return this.tree.doAddChild.apply(this, arguments);
+	},
+
+
+
+	/* Edit current node : change properties and update contents */
+	edit: function(props) {
+		dojo.lang.mixin(this, props);
+		if (props.title) {
+			this.titleNode.innerHTML = this.title;
+		}
+
+		if (props.afterLabel) {
+			this.afterLabelNode.innerHTML = this.afterLabel;
+		}
+
+		if (props.childIconSrc) {
+			this.buildChildIcon();
+		}
+
+
+	},
+
+
+	removeNode: function(){ return this.tree.removeNode.apply(this, arguments) },
+	doRemoveNode: function(){ return this.tree.doRemoveNode.apply(this, arguments) },
+
+
+	toString: function() {
+		return "["+this.widgetType+" Tree:"+this.tree+" ID:"+this.widgetId+" Title:"+this.title+"]";
+
+	}
+
+});
+
+
+
+

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeRPCController.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeRPCController.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeRPCController.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeRPCController.js Mon May 22 16:10:12 2006
@@ -0,0 +1,176 @@
+/*
+	Copyright (c) 2004-2006, 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.TreeRPCController");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.json")
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.TreeLoadingController");
+
+
+dojo.widget.tags.addParseTreeHandler("dojo:TreeRPCController");
+
+
+dojo.widget.TreeRPCController = function() {
+	dojo.widget.TreeLoadingController.call(this);
+}
+
+dojo.inherits(dojo.widget.TreeRPCController, dojo.widget.TreeLoadingController);
+
+
+dojo.lang.extend(dojo.widget.TreeRPCController, {
+	widgetType: "TreeRPCController",
+
+	/**
+	 * Make request to server about moving children.
+	 *
+	 * Request returns "true" if move succeeded,
+	 * object with error field if failed
+	 *
+	 * I can't leave DragObject floating until async request returns, need to return false/true
+	 * so making it sync way...
+	 *
+	 * Also, "loading" icon is not shown until function finishes execution, so no indication for remote request.
+	*/
+	doMove: function(child, newParent, index) {
+
+		//if (newParent.isTreeNode) newParent.markLoading();
+
+		var params = {
+			// where from
+			child: this.getInfo(child),
+			childTree: this.getInfo(child.tree),
+			// where to
+			newParent: this.getInfo(newParent),
+			newParentTree: this.getInfo(newParent.tree),
+			newIndex: index
+		};
+
+		var success;
+
+		this.runRPC({		
+			url: this.getRPCUrl('move'),
+			/* I hitch to get this.loadOkHandler */
+			load: function(response) {
+				success = this.doMoveProcessResponse(response, child, newParent, index) ;
+			},
+			sync: true,
+			lock: [child, newParent],
+			params: params
+		});
+
+
+		return success;
+	},
+
+	doMoveProcessResponse: function(response, child, newParent, index) {
+
+		if (!dojo.lang.isUndefined(response.error)) {
+			this.RPCErrorHandler("server", response.error);
+			return false;
+		}
+
+		var args = [child, newParent, index];
+		return dojo.widget.TreeLoadingController.prototype.doMove.apply(this, args);
+
+	},
+
+
+	doRemoveNode: function(node, callObj, callFunc) {
+
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree)
+		}
+
+		this.runRPC({
+				url: this.getRPCUrl('removeNode'),
+				/* I hitch to get this.loadOkHandler */
+				load: function(response) {
+					this.doRemoveNodeProcessResponse(response, node, callObj, callFunc) 
+				},
+				params: params,
+				lock: [node]
+		});
+
+	},
+
+
+	doRemoveNodeProcessResponse: function(response, node, callObj, callFunc) {
+		if (!dojo.lang.isUndefined(response.error)) {
+			this.RPCErrorHandler("server", response.error);
+			return false;
+		}
+
+		if (!response) return false;
+
+		if (response == true) {
+			/* change parent succeeded */
+			var args = [ node, callObj, callFunc ];
+			dojo.widget.TreeLoadingController.prototype.doRemoveNode.apply(this, args);
+
+			return;
+		} else if (dojo.lang.isObject(response)) {
+			dojo.raise(response.error);
+		} else {
+			dojo.raise("Invalid response "+obj)
+		}
+
+
+	},
+
+
+
+	// -----------------------------------------------------------------------------
+	//                             Create node stuff
+	// -----------------------------------------------------------------------------
+
+
+	doCreateChild: function(parent, index, output, callObj, callFunc) {
+
+			var params = {
+				tree: this.getInfo(parent.tree),
+				parent: this.getInfo(parent),
+				index: index,
+				data: output
+			}
+
+			this.runRPC({
+				url: this.getRPCUrl('createChild'),
+				load: function(response) {
+					// suggested data is dead, fresh data from server is used
+					this.doCreateChildProcessResponse( response, parent, index, callObj, callFunc) 
+				},
+				params: params,
+				lock: [parent]
+			});
+
+	},
+
+	doCreateChildProcessResponse: function(response, parent, index, callObj, callFunc) {
+
+		if (!dojo.lang.isUndefined(response.error)) {
+			this.RPCErrorHandler("server",response.error);
+			return false;
+		}
+
+		if (!dojo.lang.isObject(response)) {
+			dojo.raise("Invalid result "+response)
+		}
+
+		var args = [parent, index, response, callObj, callFunc];
+		
+		dojo.widget.TreeLoadingController.prototype.doCreateChild.apply(this, args);
+
+	}
+
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeSelector.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeSelector.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeSelector.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/TreeSelector.js Mon May 22 16:10:12 2006
@@ -0,0 +1,183 @@
+/*
+	Copyright (c) 2004-2006, 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.TreeSelector");
+
+dojo.require("dojo.widget.HtmlWidget");
+
+
+dojo.widget.tags.addParseTreeHandler("dojo:TreeSelector");
+
+
+dojo.widget.TreeSelector = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+
+	this.eventNames = {};
+
+	this.listenedTrees = [];
+
+}
+
+dojo.inherits(dojo.widget.TreeSelector, dojo.widget.HtmlWidget);
+
+
+dojo.lang.extend(dojo.widget.TreeSelector, {
+	widgetType: "TreeSelector",
+	selectedNode: null,
+
+	dieWithTree: false,
+
+	eventNamesDefault: {
+		select : "select",
+		destroy : "destroy",
+		deselect : "deselect",
+		dblselect: "dblselect" // select already selected node.. Edit or whatever
+	},
+
+	initialize: function() {
+
+		for(name in this.eventNamesDefault) {
+			if (dojo.lang.isUndefined(this.eventNames[name])) {
+				this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name];
+			}
+		}
+
+	},
+
+
+	destroy: function() {
+		dojo.event.topic.publish(this.eventNames.destroy, { source: this } );
+
+		return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments);
+	},
+
+
+	listenTree: function(tree) {
+		dojo.event.topic.subscribe(tree.eventNames.titleClick, this, "select");
+		dojo.event.topic.subscribe(tree.eventNames.iconClick, this, "select");
+		dojo.event.topic.subscribe(tree.eventNames.collapse, this, "onCollapse");
+		dojo.event.topic.subscribe(tree.eventNames.moveFrom, this, "onMoveFrom");
+		dojo.event.topic.subscribe(tree.eventNames.removeNode, this, "onRemoveNode");
+		dojo.event.topic.subscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+		/* remember all my trees to deselect when element is movedFrom them */
+		this.listenedTrees.push(tree);
+	},
+
+
+	unlistenTree: function(tree) {
+
+		dojo.event.topic.unsubscribe(tree.eventNames.titleClick, this, "select");
+		dojo.event.topic.unsubscribe(tree.eventNames.iconClick, this, "select");
+		dojo.event.topic.unsubscribe(tree.eventNames.collapse, this, "onCollapse");
+		dojo.event.topic.unsubscribe(tree.eventNames.moveFrom, this, "onMoveFrom");
+		dojo.event.topic.unsubscribe(tree.eventNames.removeNode, this, "onRemoveNode");
+		dojo.event.topic.unsubscribe(tree.eventNames.treeDestroy, this, "onTreeDestroy");
+
+
+		for(var i=0; i<this.listenedTrees.length; i++){
+           if(this.listenedTrees[i] === tree){
+                   this.listenedTrees.splice(i, 1);
+                   break;
+           }
+		}
+	},
+
+
+	onTreeDestroy: function(message) {
+
+		this.unlistenTree(message.source);
+
+		if (this.dieWithTree) {
+			//dojo.debug("Killing myself "+this.widgetId);
+			this.destroy();
+			//dojo.debug("done");
+		}
+	},
+
+
+	// deselect node if parent is collapsed
+	onCollapse: function(message) {
+		if (!this.selectedNode) return;
+
+		var node = message.source;
+		var parent = this.selectedNode.parent;
+		while (parent !== node && parent.isTreeNode) {
+			parent = parent.parent;
+		}
+		if (parent.isTreeNode) {
+			this.deselect();
+		}
+	},
+
+
+
+	select: function(message) {
+		var node = message.source;
+		var e = message.event;
+
+		if (this.selectedNode === node) {
+			dojo.event.topic.publish(this.eventNames.dblselect, { node: node });
+			return;
+		}
+
+		if (this.selectedNode) {
+			this.deselect();
+		}
+
+		this.doSelect(node);
+
+		dojo.event.topic.publish(this.eventNames.select, {node: node} );
+	},
+
+	/**
+	 * Deselect node if target tree is out of our concern
+	 */
+	onMoveFrom: function(message) {
+		if (message.child !== this.selectedNode) {
+			return;
+		}
+
+		if (!dojo.lang.inArray(this.listenedTrees, message.newTree)) {
+			this.deselect();
+		}
+	},
+
+	onRemoveNode: function(message) {
+		if (message.child !== this.selectedNode) {
+			return;
+		}
+
+		this.deselect();
+	},
+
+	doSelect: function(node){
+
+		node.markSelected();
+
+		this.selectedNode = node;
+	},
+
+	deselect: function(){
+
+		var node = this.selectedNode;
+
+		this.selectedNode = null;
+		node.unMarkSelected();
+		dojo.event.topic.publish(this.eventNames.deselect, {node: node} );
+
+	}
+
+});
+
+
+

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Widget.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Widget.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Widget.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Widget.js Mon May 22 16:10:12 2006
@@ -0,0 +1,547 @@
+/*
+	Copyright (c) 2004-2006, 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.Widget");
+dojo.provide("dojo.widget.tags");
+
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.lang.declare");
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.event.*");
+
+dojo.declare("dojo.widget.Widget", null, {
+	initializer: function() {								 
+		// these properties aren't primitives and need to be created on a per-item
+		// basis.
+		this.children = [];
+		// this.selection = new dojo.widget.Selection();
+		// FIXME: need to replace this with context menu stuff
+		this.extraArgs = {};
+	},
+	// FIXME: need to be able to disambiguate what our rendering context is
+	//        here!
+	//
+	// needs to be a string with the end classname. Every subclass MUST
+	// over-ride.
+	//
+	// base widget properties
+	parent: null,
+	// obviously, top-level and modal widgets should set these appropriately
+	isTopLevel:  false,
+	isModal: false,
+
+	isEnabled: true,
+	isHidden: false,
+	isContainer: false, // can we contain other widgets?
+	widgetId: "",
+	widgetType: "Widget", // used for building generic widgets
+
+	toString: function() {
+		return '[Widget ' + this.widgetType + ', ' + (this.widgetId || 'NO ID') + ']';
+	},
+
+	repr: function(){
+		return this.toString();
+	},
+
+	enable: function(){
+		// should be over-ridden
+		this.isEnabled = true;
+	},
+
+	disable: function(){
+		// should be over-ridden
+		this.isEnabled = false;
+	},
+
+	hide: function(){
+		// should be over-ridden
+		this.isHidden = true;
+	},
+
+	show: function(){
+		// should be over-ridden
+		this.isHidden = false;
+	},
+
+	onResized: function(){
+		// Clients should override this function to do special processing,
+		// then call this.notifyChildrenOfResize() to notify children of resize
+		this.notifyChildrenOfResize();
+	},
+	
+	notifyChildrenOfResize: function(){
+		for(var i=0; i<this.children.length; i++){
+			var child = this.children[i];
+			//dojo.debug(this.widgetId + " resizing child " + child.widgetId);
+			if( child.onResized ){
+				child.onResized();
+			}
+		}
+	},
+
+	create: function(args, fragment, parentComp){
+		// dojo.debug(this.widgetType, "create");
+		this.satisfyPropertySets(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> mixInProperties");
+		this.mixInProperties(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> postMixInProperties");
+		this.postMixInProperties(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> dojo.widget.manager.add");
+		dojo.widget.manager.add(this);
+		// dojo.debug(this.widgetType, "-> buildRendering");
+		this.buildRendering(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> initialize");
+		this.initialize(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> postInitialize");
+		this.postInitialize(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "-> postCreate");
+		this.postCreate(args, fragment, parentComp);
+		// dojo.debug(this.widgetType, "done!");
+		return this;
+	},
+
+	// Destroy this widget and it's descendants
+	destroy: function(finalize){
+		// FIXME: this is woefully incomplete
+		this.destroyChildren();
+		this.uninitialize();
+		this.destroyRendering(finalize);
+		dojo.widget.manager.removeById(this.widgetId);
+	},
+
+	// Destroy the children of this widget, and their descendents
+	destroyChildren: function(){
+		while(this.children.length > 0){
+			var tc = this.children[0];
+			this.removeChild(tc);
+			tc.destroy();
+		}
+	},
+
+	getChildrenOfType: function(type, recurse){
+		var ret = [];
+		var isFunc = dojo.lang.isFunction(type);
+		if(!isFunc){
+			type = type.toLowerCase();
+		}
+		for(var x=0; x<this.children.length; x++){
+			if(isFunc){
+				if(this.children[x] instanceof type){
+					ret.push(this.children[x]);
+				}
+			}else{
+				if(this.children[x].widgetType.toLowerCase() == type){
+					ret.push(this.children[x]);
+				}
+			}
+			if(recurse){
+				ret = ret.concat(this.children[x].getChildrenOfType(type, recurse));
+			}
+		}
+		return ret;
+	},
+
+	getDescendants: function(){
+		var result = [];
+		var stack = [this];
+		var elem;
+		while (elem = stack.pop()){
+			result.push(elem);
+			dojo.lang.forEach(elem.children, function(elem) { stack.push(elem); });
+		}
+		return result;
+	},
+
+	satisfyPropertySets: function(args){
+		// dojo.profile.start("satisfyPropertySets");
+		// get the default propsets for our component type
+		/*
+		var typePropSets = []; // FIXME: need to pull these from somewhere!
+		var localPropSets = []; // pull out propsets from the parser's return structure
+
+		// for(var x=0; x<args.length; x++){
+		// }
+
+		for(var x=0; x<typePropSets.length; x++){
+		}
+
+		for(var x=0; x<localPropSets.length; x++){
+		}
+		*/
+		// dojo.profile.end("satisfyPropertySets");
+		
+		return args;
+	},
+
+	mixInProperties: function(args, frag){
+		if((args["fastMixIn"])||(frag["fastMixIn"])){
+			// dojo.profile.start("mixInProperties_fastMixIn");
+			// fast mix in assumes case sensitivity, no type casting, etc...
+			// dojo.lang.mixin(this, args);
+			for(var x in args){
+				this[x] = args[x];
+			}
+			// dojo.profile.end("mixInProperties_fastMixIn");
+			return;
+		}
+		// dojo.profile.start("mixInProperties");
+		/*
+		 * the actual mix-in code attempts to do some type-assignment based on
+		 * PRE-EXISTING properties of the "this" object. When a named property
+		 * of a propset is located, it is first tested to make sure that the
+		 * current object already "has one". Properties which are undefined in
+		 * the base widget are NOT settable here. The next step is to try to
+		 * determine type of the pre-existing property. If it's a string, the
+		 * property value is simply assigned. If a function, the property is
+		 * replaced with a "new Function()" declaration. If an Array, the
+		 * system attempts to split the string value on ";" chars, and no
+		 * further processing is attempted (conversion of array elements to a
+		 * integers, for instance). If the property value is an Object
+		 * (testObj.constructor === Object), the property is split first on ";"
+		 * chars, secondly on ":" chars, and the resulting key/value pairs are
+		 * assigned to an object in a map style. The onus is on the property
+		 * user to ensure that all property values are converted to the
+		 * expected type before usage.
+		 */
+
+		var undef;
+
+		// NOTE: we cannot assume that the passed properties are case-correct
+		// (esp due to some browser bugs). Therefore, we attempt to locate
+		// properties for assignment regardless of case. This may cause
+		// problematic assignments and bugs in the future and will need to be
+		// documented with big bright neon lights.
+
+		// FIXME: fails miserably if a mixin property has a default value of null in 
+		// a widget
+
+		// NOTE: caching lower-cased args in the prototype is only 
+		// acceptable if the properties are invariant.
+		// if we have a name-cache, get it
+		var lcArgs = dojo.widget.lcArgsCache[this.widgetType];
+		if ( lcArgs == null ){
+			// build a lower-case property name cache if we don't have one
+			lcArgs = {};
+			for(var y in this){
+				lcArgs[((new String(y)).toLowerCase())] = y;
+			}
+			dojo.widget.lcArgsCache[this.widgetType] = lcArgs;
+		}
+		var visited = {};
+		for(var x in args){
+			if(!this[x]){ // check the cache for properties
+				var y = lcArgs[(new String(x)).toLowerCase()];
+				if(y){
+					args[y] = args[x];
+					x = y; 
+				}
+			}
+			if(visited[x]){ continue; }
+			visited[x] = true;
+			if((typeof this[x]) != (typeof undef)){
+				if(typeof args[x] != "string"){
+					this[x] = args[x];
+				}else{
+					if(dojo.lang.isString(this[x])){
+						this[x] = args[x];
+					}else if(dojo.lang.isNumber(this[x])){
+						this[x] = new Number(args[x]); // FIXME: what if NaN is the result?
+					}else if(dojo.lang.isBoolean(this[x])){
+						this[x] = (args[x].toLowerCase()=="false") ? false : true;
+					}else if(dojo.lang.isFunction(this[x])){
+
+						// FIXME: need to determine if always over-writing instead
+						// of attaching here is appropriate. I suspect that we
+						// might want to only allow attaching w/ action items.
+						
+						// RAR, 1/19/05: I'm going to attach instead of
+						// over-write here. Perhaps function objects could have
+						// some sort of flag set on them? Or mixed-into objects
+						// could have some list of non-mutable properties
+						// (although I'm not sure how that would alleviate this
+						// particular problem)? 
+
+						// this[x] = new Function(args[x]);
+
+						// after an IRC discussion last week, it was decided
+						// that these event handlers should execute in the
+						// context of the widget, so that the "this" pointer
+						// takes correctly.
+						
+						// argument that contains no punctuation other than . is 
+						// considered a function spec, not code
+						if(args[x].search(/[^\w\.]+/i) == -1){
+							this[x] = dojo.evalObjPath(args[x], false);
+						}else{
+							var tn = dojo.lang.nameAnonFunc(new Function(args[x]), this);
+							dojo.event.connect(this, x, this, tn);
+						}
+					}else if(dojo.lang.isArray(this[x])){ // typeof [] == "object"
+						this[x] = args[x].split(";");
+					} else if (this[x] instanceof Date) {
+						this[x] = new Date(Number(args[x])); // assume timestamp
+					}else if(typeof this[x] == "object"){ 
+						// FIXME: should we be allowing extension here to handle
+						// other object types intelligently?
+
+						// if we defined a URI, we probablt want to allow plain strings
+						// to override it
+						if (this[x] instanceof dojo.uri.Uri){
+
+							this[x] = args[x];
+						}else{
+
+							// FIXME: unlike all other types, we do not replace the
+							// object with a new one here. Should we change that?
+							var pairs = args[x].split(";");
+							for(var y=0; y<pairs.length; y++){
+								var si = pairs[y].indexOf(":");
+								if((si != -1)&&(pairs[y].length>si)){
+									this[x][pairs[y].substr(0, si).replace(/^\s+|\s+$/g, "")] = pairs[y].substr(si+1);
+								}
+							}
+						}
+					}else{
+						// the default is straight-up string assignment. When would
+						// we ever hit this?
+						this[x] = args[x];
+					}
+				}
+			}else{
+				// collect any extra 'non mixed in' args
+				this.extraArgs[x.toLowerCase()] = args[x];
+			}
+		}
+		// dojo.profile.end("mixInProperties");
+	},
+	
+	postMixInProperties: function(){
+	},
+
+	initialize: function(args, frag){
+		// dojo.unimplemented("dojo.widget.Widget.initialize");
+		return false;
+	},
+
+	postInitialize: function(args, frag){
+		return false;
+	},
+
+	postCreate: function(args, frag){
+		return false;
+	},
+
+	uninitialize: function(){
+		// dojo.unimplemented("dojo.widget.Widget.uninitialize");
+		return false;
+	},
+
+	buildRendering: function(){
+		// SUBCLASSES MUST IMPLEMENT
+		dojo.unimplemented("dojo.widget.Widget.buildRendering, on "+this.toString()+", ");
+		return false;
+	},
+
+	destroyRendering: function(){
+		// SUBCLASSES MUST IMPLEMENT
+		dojo.unimplemented("dojo.widget.Widget.destroyRendering");
+		return false;
+	},
+
+	cleanUp: function(){
+		// SUBCLASSES MUST IMPLEMENT
+		dojo.unimplemented("dojo.widget.Widget.cleanUp");
+		return false;
+	},
+
+	addedTo: function(parent){
+		// this is just a signal that can be caught
+	},
+
+	addChild: function(child){
+		// SUBCLASSES MUST IMPLEMENT
+		dojo.unimplemented("dojo.widget.Widget.addChild");
+		return false;
+	},
+
+	// Detach the given child widget from me, but don't destroy it
+	removeChild: function(widget){
+		for(var x=0; x<this.children.length; x++){
+			if(this.children[x] === widget){
+				this.children.splice(x, 1);
+				break;
+			}
+		}
+		return widget;
+	},
+
+	resize: function(width, height){
+		// both width and height may be set as percentages. The setWidth and
+		// setHeight  functions attempt to determine if the passed param is
+		// specified in percentage or native units. Integers without a
+		// measurement are assumed to be in the native unit of measure.
+		this.setWidth(width);
+		this.setHeight(height);
+	},
+
+	setWidth: function(width){
+		if((typeof width == "string")&&(width.substr(-1) == "%")){
+			this.setPercentageWidth(width);
+		}else{
+			this.setNativeWidth(width);
+		}
+	},
+
+	setHeight: function(height){
+		if((typeof height == "string")&&(height.substr(-1) == "%")){
+			this.setPercentageHeight(height);
+		}else{
+			this.setNativeHeight(height);
+		}
+	},
+
+	setPercentageHeight: function(height){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	setNativeHeight: function(height){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	setPercentageWidth: function(width){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	setNativeWidth: function(width){
+		// SUBCLASSES MUST IMPLEMENT
+		return false;
+	},
+
+	getPreviousSibling: function() {
+		var idx = this.getParentIndex();
+ 
+		 // first node is idx=0 not found is idx<0
+		if (idx<=0) return null;
+ 
+		return this.getSiblings()[idx-1];
+	},
+ 
+	getSiblings: function() {
+		return this.parent.children;
+	},
+ 
+	getParentIndex: function() {
+		return dojo.lang.indexOf( this.getSiblings(), this, true);
+	},
+ 
+	getNextSibling: function() {
+ 
+		var idx = this.getParentIndex();
+ 
+		if (idx == this.getSiblings().length-1) return null; // last node
+		if (idx < 0) return null; // not found
+ 
+		return this.getSiblings()[idx+1];
+ 
+	}
+});
+
+// Lower case name cache: listing of the lower case elements in each widget.
+// We can't store the lcArgs in the widget itself because if B subclasses A,
+// then B.prototype.lcArgs might return A.prototype.lcArgs, which is not what we
+// want
+dojo.widget.lcArgsCache = {};
+
+// TODO: should have a more general way to add tags or tag libraries?
+// TODO: need a default tags class to inherit from for things like getting propertySets
+// TODO: parse properties/propertySets into component attributes
+// TODO: parse subcomponents
+// TODO: copy/clone raw markup fragments/nodes as appropriate
+dojo.widget.tags = {};
+dojo.widget.tags.addParseTreeHandler = function(type){
+	var ltype = type.toLowerCase();
+	this[ltype] = function(fragment, widgetParser, parentComp, insertionIndex, localProps){ 
+		return dojo.widget.buildWidgetFromParseTree(ltype, fragment, widgetParser, parentComp, insertionIndex, localProps);
+	}
+}
+dojo.widget.tags.addParseTreeHandler("dojo:widget");
+
+dojo.widget.tags["dojo:propertyset"] = function(fragment, widgetParser, parentComp){
+	// FIXME: Is this needed?
+	// FIXME: Not sure that this parses into the structure that I want it to parse into...
+	// FIXME: add support for nested propertySets
+	var properties = widgetParser.parseProperties(fragment["dojo:propertyset"]);
+}
+
+// FIXME: need to add the <dojo:connect />
+dojo.widget.tags["dojo:connect"] = function(fragment, widgetParser, parentComp){
+	var properties = widgetParser.parseProperties(fragment["dojo:connect"]);
+}
+
+// FIXME: if we know the insertion point (to a reasonable location), why then do we:
+//	- create a template node
+//	- clone the template node
+//	- render the clone and set properties
+//	- remove the clone from the render tree
+//	- place the clone
+// this is quite dumb
+dojo.widget.buildWidgetFromParseTree = function(type, frag, 
+												parser, parentComp, 
+												insertionIndex, localProps){
+	var stype = type.split(":");
+	stype = (stype.length == 2) ? stype[1] : type;
+	// FIXME: we don't seem to be doing anything with this!
+	// var propertySets = parser.getPropertySets(frag);
+	var localProperties = localProps || parser.parseProperties(frag["dojo:"+stype]);
+	// var tic = new Date();
+	var twidget = dojo.widget.manager.getImplementation(stype);
+	if(!twidget){
+		throw new Error("cannot find \"" + stype + "\" widget");
+	}else if (!twidget.create){
+		throw new Error("\"" + stype + "\" widget object does not appear to implement *Widget");
+	}
+	localProperties["dojoinsertionindex"] = insertionIndex;
+	// FIXME: we loose no less than 5ms in construction!
+	var ret = twidget.create(localProperties, frag, parentComp);
+	// dojo.debug(new Date() - tic);
+	return ret;
+}
+
+/*
+ * it would be best to be able to call defineWidget for any widget namespace
+ */
+dojo.widget.defineWidget = function(widgetClass /*string*/, superclass /*function*/, props /*object*/, renderer /*string*/, ctor /*function*/){
+	// widgetClass takes the form foo.bar.baz<.renderer>.WidgetName (e.g. foo.bar.baz.WidgetName or foo.bar.baz.html.WidgetName)
+	var namespace = widgetClass.split(".");
+	var type = namespace.pop(); // type <= WidgetName, namespace <= foo.bar.baz<.renderer>
+	if(renderer){
+		// FIXME: could just be namespace.pop(), unless there can be foo.bar.baz.html.zot.WidgetName
+		while ((namespace.length)&&(namespace.pop() != renderer)); // namespace <= foo.bar.baz
+	}
+	namespace = namespace.join(".");
+
+	dojo.widget.manager.registerWidgetPackage(namespace);
+	dojo.widget.tags.addParseTreeHandler("dojo:"+type.toLowerCase());
+
+	if(!props){ props = {}; }
+	props.widgetType = type;
+
+	if((!ctor)&&(props["classConstructor"])){
+		ctor = props.classConstructor;
+		delete props.classConstructor;
+	}
+	dojo.declare(widgetClass, superclass, props, ctor);
+}

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Wizard.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Wizard.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Wizard.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/Wizard.js Mon May 22 16:10:12 2006
@@ -0,0 +1,210 @@
+/*
+	Copyright (c) 2004-2006, 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.Wizard");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutContainer");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+//////////////////////////////////////////
+// WizardContainer -- a set of panels
+//////////////////////////////////////////
+dojo.widget.WizardContainer = function() {
+	dojo.widget.html.LayoutContainer.call(this);
+}
+dojo.inherits(dojo.widget.WizardContainer, dojo.widget.html.LayoutContainer);
+
+dojo.lang.extend(dojo.widget.WizardContainer, {
+
+	widgetType: "WizardContainer",
+
+	labelPosition: "top",
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/Wizard.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/Wizard.css"),
+
+	selected: null,		// currently selected panel
+	wizardNode: null, // the outer wizard node
+	wizardPanelContainerNode: null, // the container for the panels
+	wizardControlContainerNode: null, // the container for the wizard controls
+	previousButton: null, // the previous button
+	nextButton: null, // the next button
+	cancelButton: null, // the cancel button
+	doneButton: null, // the done button
+	nextButtonLabel: "next",
+	previousButtonLabel: "previous",
+	cancelButtonLabel: "cancel",
+	doneButtonLabel: "done",
+	cancelFunction : "",
+
+	hideDisabledButtons: false,
+
+	fillInTemplate: function(args, frag){
+		dojo.event.connect(this.nextButton, "onclick", this, "nextPanel");
+		dojo.event.connect(this.previousButton, "onclick", this, "previousPanel");
+		if (this.cancelFunction){
+			dojo.event.connect(this.cancelButton, "onclick", this.cancelFunction);
+		}else{
+			this.cancelButton.style.display = "none";
+		}
+		dojo.event.connect(this.doneButton, "onclick", this, "done");
+		this.nextButton.value = this.nextButtonLabel;
+		this.previousButton.value = this.previousButtonLabel;
+		this.cancelButton.value = this.cancelButtonLabel;
+		this.doneButton.value = this.doneButtonLabel;
+	},
+
+	checkButtons: function(){
+		var lastStep = !this.hasNextPanel();
+		this.nextButton.disabled = lastStep;
+		this.setButtonClass(this.nextButton);
+		if(this.selected.doneFunction){
+			this.doneButton.style.display = "";
+			// hide the next button if this is the last one and we have a done function
+			if(lastStep){
+				this.nextButton.style.display = "none";
+			}
+		}else{
+			this.doneButton.style.display = "none";
+		}
+		this.previousButton.disabled = ((!this.hasPreviousPanel()) || (!this.selected.canGoBack));
+		this.setButtonClass(this.previousButton);
+	},
+
+	setButtonClass: function(button){
+		if(!this.hideDisabledButtons){
+			button.style.display = "";
+			dojo.html.setClass(button, button.disabled ? "WizardButtonDisabled" : "WizardButton");
+		}else{
+			button.style.display = button.disabled ? "none" : "";
+		}
+	},
+
+	registerChild: function(panel, insertionIndex){
+		dojo.widget.WizardContainer.superclass.registerChild.call(this, panel, insertionIndex);
+		this.wizardPanelContainerNode.appendChild(panel.domNode);
+		panel.hide();
+
+		if(!this.selected){
+			this.onSelected(panel);
+		}
+		this.checkButtons();
+	},
+
+	onSelected: function(panel){
+		// Deselect old panel and select new one
+		if(this.selected ){
+			if (this.selected.checkPass()) {
+				this.selected.hide();
+			} else {
+				return;
+			}
+		}
+		panel.show();
+		this.selected = panel;
+	},
+
+	getPanels: function() {
+		return this.getChildrenOfType("WizardPane", false);
+	},
+
+	selectedIndex: function() {
+		if (this.selected) {
+			return dojo.lang.indexOf(this.getPanels(), this.selected);
+		}
+		return -1;
+	},
+
+	nextPanel: function() {
+		var selectedIndex = this.selectedIndex();
+		if ( selectedIndex > -1 ) {
+			var childPanels = this.getPanels();
+			if (childPanels[selectedIndex + 1]) {
+				this.onSelected(childPanels[selectedIndex + 1]);
+			}
+		}
+		this.checkButtons();
+	},
+
+	previousPanel: function() {
+		var selectedIndex = this.selectedIndex();
+		if ( selectedIndex > -1 ) {
+			var childPanels = this.getPanels();
+			if (childPanels[selectedIndex - 1]) {
+				this.onSelected(childPanels[selectedIndex - 1]);
+			}
+		}
+		this.checkButtons();
+	},
+
+	hasNextPanel: function() {
+		var selectedIndex = this.selectedIndex();
+		return (selectedIndex < (this.getPanels().length - 1));
+	},
+
+	hasPreviousPanel: function() {
+		var selectedIndex = this.selectedIndex();
+		return (selectedIndex > 0);
+	},
+
+	done: function() {
+		this.selected.done();
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:WizardContainer");
+
+//////////////////////////////////////////
+// WizardPane -- a panel in a wizard
+//////////////////////////////////////////
+dojo.widget.WizardPane = function() {
+	dojo.widget.html.ContentPane.call(this);
+}
+dojo.inherits(dojo.widget.WizardPane, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.WizardPane, {
+	widgetType: "WizardPane",
+
+	canGoBack: true,
+
+	passFunction: "",
+	doneFunction: "",
+
+	fillInTemplate: function(args, frag) {
+		if (this.passFunction) {
+			this.passFunction = dj_global[this.passFunction];
+		}
+		if (this.doneFunction) {
+			this.doneFunction = dj_global[this.doneFunction];
+		}
+	},
+
+	checkPass: function() {
+		if (this.passFunction && dojo.lang.isFunction(this.passFunction)) {
+			var failMessage = this.passFunction();
+			if (failMessage) {
+				alert(failMessage);
+				return false;
+			}
+		}
+		return true;
+	},
+
+	done: function() {
+		if (this.doneFunction && dojo.lang.isFunction(this.doneFunction)) {
+			this.doneFunction();
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:WizardPane");

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/YahooMap.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/YahooMap.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/YahooMap.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/YahooMap.js Mon May 22 16:10:12 2006
@@ -0,0 +1,27 @@
+/*
+	Copyright (c) 2004-2006, 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.YahooMap");
+dojo.provide("dojo.widget.YahooMap.Controls");
+dojo.require("dojo.widget.*");
+
+dojo.widget.defineWidget(
+	"dojo.widget.YahooMap",
+	dojo.widget.Widget,
+	{ isContainer: false }
+);
+
+dojo.widget.YahooMap.Controls={
+	MapType:"maptype",
+	Pan:"pan",
+	ZoomLong:"zoomlong",
+	ZoomShort:"zoomshort"
+};
+dojo.requireAfterIf("html", "dojo.widget.html.YahooMap");

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/__package__.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/__package__.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/__package__.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/__package__.js Mon May 22 16:10:12 2006
@@ -0,0 +1,23 @@
+/*
+	Copyright (c) 2004-2006, 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.kwCompoundRequire({
+	common: ["dojo.xml.Parse", 
+			 "dojo.widget.Widget", 
+			 "dojo.widget.Parse", 
+			 "dojo.widget.Manager"],
+	browser: ["dojo.widget.DomWidget",
+			  "dojo.widget.HtmlWidget"],
+	dashboard: ["dojo.widget.DomWidget",
+			  "dojo.widget.HtmlWidget"],
+	svg: 	 ["dojo.widget.SvgWidget"],
+	rhino: 	 ["dojo.widget.SwtWidget"]
+});
+dojo.provide("dojo.widget.*");

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/AccordionPane.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/AccordionPane.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/AccordionPane.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/AccordionPane.js Mon May 22 16:10:12 2006
@@ -0,0 +1,98 @@
+/*
+	Copyright (c) 2004-2006, 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.html.AccordionPane");
+dojo.require("dojo.widget.TitlePane");
+
+dojo.widget.html.AccordionPane = function(){
+
+	dojo.widget.html.TitlePane.call(this);
+	this.widgetType = "AccordionPane";
+
+	this.open=false;
+	this.allowCollapse=true;
+	this.label="";
+	this.open=false;
+
+	this.labelNodeClass="";
+	this.containerNodeClass="";
+}
+
+dojo.inherits(dojo.widget.html.AccordionPane, dojo.widget.html.TitlePane);
+
+dojo.lang.extend(dojo.widget.html.AccordionPane, {
+        postCreate: function() {
+                dojo.widget.html.AccordionPane.superclass.postCreate.call(this);
+		this.domNode.widgetType=this.widgetType;
+		this.setSizes();
+		dojo.html.addClass(this.labelNode, this.labelNodeClass);
+		dojo.html.disableSelection(this.labelNode);
+		dojo.html.addClass(this.containerNode, this.containerNodeClass);
+        },
+
+	collapse: function() {
+		//dojo.fx.html.wipeOut(this.containerNode,250);
+		//var anim = dojo.fx.html.wipe(this.containerNode, 1000, this.containerNode.offsetHeight, 0, null, true);
+		this.containerNode.style.display="none";
+		this.open=false;
+	},
+
+	expand: function() {
+		//dojo.fx.html.wipeIn(this.containerNode,250);
+		this.containerNode.style.display="block";
+		//var anim = dojo.fx.html.wipe(this.containerNode, 1000, 0, this.containerNode.scrollHeight, null, true);
+		this.open=true;
+	},
+
+	getCollapsedHeight: function() {
+		return dojo.style.getOuterHeight(this.labelNode)+1;
+	},
+
+	setSizes: function() {
+		var siblings = this.domNode.parentNode.childNodes;
+		var height=dojo.style.getInnerHeight(this.domNode.parentNode)-this.getCollapsedHeight();
+
+		this.siblingWidgets = [];
+	
+		for (var x=0; x<siblings.length; x++) {
+			if (siblings[x].widgetType==this.widgetType) {
+				if (this.domNode != siblings[x]) {
+					var ap = dojo.widget.byNode(siblings[x]);
+					this.siblingWidgets.push(ap);
+					height -= ap.getCollapsedHeight();
+				}
+			}
+		}
+	
+		for (var x=0; x<this.siblingWidgets.length; x++) {
+			dojo.style.setOuterHeight(this.siblingWidgets[x].containerNode,height);
+		}
+
+		dojo.style.setOuterHeight(this.containerNode,height);
+	},
+
+	onLabelClick: function() {
+		this.setSizes();
+		if (!this.open) { 
+			for (var x=0; x<this.siblingWidgets.length;x++) {
+				if (this.siblingWidgets[x].open) {
+					this.siblingWidgets[x].collapse();
+				}
+			}
+			this.expand();
+		} else {
+			if (this.allowCollapse) {
+				this.collapse();
+			}
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:AccordionPane");

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Button2.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Button2.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Button2.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Button2.js Mon May 22 16:10:12 2006
@@ -0,0 +1,25 @@
+/*
+	Copyright (c) 2004-2006, 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
+*/
+
+// this is a stub that will be removed in 0.4, see ../Button2.html for details
+
+dojo.provide("dojo.widget.html.Button2");
+
+dojo.widget.html.Button2 = function(){}
+dojo.inherits(dojo.widget.html.Button2, dojo.widget.html.Button);
+dojo.lang.extend(dojo.widget.html.Button2, { widgetType: "Button2" });
+
+dojo.widget.html.DropDownButton2 = function(){}
+dojo.inherits(dojo.widget.html.DropDownButton2, dojo.widget.html.DropDownButton);
+dojo.lang.extend(dojo.widget.html.DropDownButton2, { widgetType: "DropDownButton2" });
+
+dojo.widget.html.ComboButton2 = function(){}
+dojo.inherits(dojo.widget.html.ComboButton2, dojo.widget.html.ComboButton);
+dojo.lang.extend(dojo.widget.html.ComboButton2, { widgetType: "ComboButton2" });
\ No newline at end of file

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Checkbox.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Checkbox.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Checkbox.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Checkbox.js Mon May 22 16:10:12 2006
@@ -0,0 +1,105 @@
+/*
+	Copyright (c) 2004-2006, 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.Checkbox");
+dojo.provide("dojo.widget.html.Checkbox");
+
+// FIXME: the input doesn't get taken out of the tab list (i think)
+// FIXME: the image doesn't get into the tab list (needs to steal the tabindex value from the input)
+
+dojo.widget.html.Checkbox = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.Checkbox, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.Checkbox, {
+	widgetType: "Checkbox",
+
+	_testImg: null,
+
+	_events: [
+		"onclick",
+		"onfocus",
+		"onblur",
+		"onselect",
+		"onchange",
+		"onclick",
+		"ondblclick",
+		"onmousedown",
+		"onmouseup",
+		"onmouseover",
+		"onmousemove",
+		"onmouseout",
+		"onkeypress",
+		"onkeydown",
+		"onkeyup"
+	],
+
+	srcOn: dojo.uri.dojoUri('src/widget/templates/check_on.gif'),
+	srcOff: dojo.uri.dojoUri('src/widget/templates/check_off.gif'),
+
+	fillInTemplate: function(){
+
+		// FIXME: if images are disabled, we DON'T want to swap out the element
+		// we can use the usual 'load image to check' trick
+		// i don't know what image we can check yet, so we'll skip this for now...
+
+		// this._testImg = document.createElement("img");
+		// document.body.appendChild(this._testImg);
+		// this._testImg.src = "spacer.gif?cachebust=" + new Date().valueOf();
+		// dojo.connect(this._testImg, 'onload', this, 'onImagesLoaded');
+
+		this.onImagesLoaded();
+	},
+
+	onImagesLoaded: function(){
+
+		// FIXME: if we actually check for loading images, remove the thing here
+		// document.body.removeChild(this._testImg);
+
+		// 'hide' the checkbox
+		this.domNode.style.position = "absolute";
+		this.domNode.style.left = "-9000px";
+
+		// create a replacement image
+		this.imgNode = document.createElement("img");
+		dojo.html.addClass(this.imgNode, "dojoHtmlCheckbox");
+		this.updateImgSrc();
+		dojo.event.connect(this.imgNode, 'onclick', this, 'onClick');
+		dojo.event.connect(this.domNode, 'onchange', this, 'onChange');
+		this.domNode.parentNode.insertBefore(this.imgNode, this.domNode.nextSibling)
+
+		// real ugly - make sure the image has all the events that the checkbox did
+		for(var i=0; i<this._events.length; i++){
+			if(this.domNode[this._events[i]]){
+				dojo.event.connect(	this.imgNode, this._events[i], 
+									this.domNode[this._events[i]]);
+			}
+		}
+	},
+
+	onClick: function(){
+
+		this.domNode.checked = !this.domNode.checked ? true : false;
+		this.updateImgSrc();
+	},
+
+	onChange: function(){
+
+		this.updateImgSrc();
+	},
+
+	updateImgSrc: function(){
+
+		this.imgNode.src = this.domNode.checked ? this.srcOn : this.srcOff;
+	}
+});
+