You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ca...@apache.org on 2007/01/11 23:36:18 UTC

svn commit: r495409 [41/47] - in /myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource: ./ src/ src/animation/ src/cal/ src/charting/ src/charting/svg/ src/charting/vml/ src/collections/ src/crypto/ src/data/ src/data/...

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeRpcControllerV3.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeRpcControllerV3.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeRpcControllerV3.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeRpcControllerV3.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,429 @@
+/*
+	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.TreeRpcControllerV3");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.json")
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.TreeLoadingControllerV3");
+
+dojo.widget.defineWidget(
+	"dojo.widget.TreeRpcControllerV3",
+	dojo.widget.TreeLoadingControllerV3,
+{
+	// TODO: do something with addChild / setChild, so that RpcController become able
+	// to hook on this and report to server
+
+	extraRpcOnEdit: false,
+				
+	/**
+	 * 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, sync){
+
+		//if (newParent.isTreeNode) newParent.markLoading();
+
+		
+		var params = {
+			// where from
+			child: this.getInfo(child),
+			childTree: this.getInfo(child.tree),
+			oldParent: this.getInfo(child.parent),
+			oldParentTree: this.getInfo(child.parent.tree),
+			// where to
+			newParent: this.getInfo(newParent),
+			newParentTree: this.getInfo(newParent.tree),
+			newIndex: index
+		};
+
+
+		var deferred = this.runRpc({		
+			url: this.getRpcUrl('move'),
+			sync: sync,			
+			params: params
+		});
+
+		var _this = this;
+		var args = arguments;	
+		
+		//deferred.addCallback(function(res) { dojo.debug("doMove fired "+res); return res});
+		
+		deferred.addCallback(function() {			
+			dojo.widget.TreeBasicControllerV3.prototype.doMove.apply(_this,args);
+		});
+
+		
+		return deferred;
+	},
+
+	// -------------- detach
+	
+	prepareDetach: function(node, sync) {
+		var deferred = this.startProcessing(node);		
+		return deferred;
+	},
+	
+	finalizeDetach: function(node) {
+		this.finishProcessing(node);
+	},
+
+	doDetach: function(node, sync){
+
+		
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree)
+		}
+
+		var deferred = this.runRpc({
+			url: this.getRpcUrl('detach'),
+			sync: sync,
+			params: params			
+		});
+		
+		
+		var _this = this;
+		var args = arguments;
+		
+		deferred.addCallback(function() {			
+			dojo.widget.TreeBasicControllerV3.prototype.doDetach.apply(_this,args);
+		});
+		
+						
+		return deferred;
+
+	},
+
+	// -------------------------- Inline edit node ---------------------	
+
+	/**
+	 * send edit start request if needed
+	 * useful for server-side locking 
+	 */
+	requestEditConfirmation: function(node, action, sync) {
+		if (!this.extraRpcOnEdit) {			
+			return dojo.Deferred.prototype.makeCalled();
+		}
+	
+		//dojo.debug("requestEditConfirmation "+node+" "+action);
+		
+		var _this = this;
+	
+		var deferred = this.startProcessing(node);
+			
+		//dojo.debug("startProcessing "+node);
+		
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree)
+		}
+		
+		deferred.addCallback(function() {
+			//dojo.debug("add action on requestEditConfirmation "+action);
+			return _this.runRpc({
+				url: _this.getRpcUrl(action),
+				sync: sync,
+				params: params			
+			});
+		});
+		
+		
+		deferred.addBoth(function(r) {
+			//dojo.debug("finish rpc with "+r);
+			_this.finishProcessing(node);
+			return r;
+		});
+	
+		return deferred;
+	},
+	
+	editLabelSave: function(node, newContent, sync) {
+		var deferred = this.startProcessing(node);
+						
+		var _this = this;
+		
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree),
+			newContent: newContent
+		}
+		
+	
+		deferred.addCallback(function() {
+			return _this.runRpc({
+				url: _this.getRpcUrl('editLabelSave'),
+				sync: sync,
+				params: params			
+			});
+		});
+		
+		
+		deferred.addBoth(function(r) {
+			_this.finishProcessing(node);
+			return r;
+		});
+	
+		return deferred;
+	},
+	
+	editLabelStart: function(node, sync) {		
+		if (!this.canEditLabel(node)) {
+			return false;
+		}
+		
+		var _this = this;
+		
+		if (!this.editor.isClosed()) {
+			//dojo.debug("editLabelStart editor open");
+			var deferred = this.editLabelFinish(this.editor.saveOnBlur, sync);
+			deferred.addCallback(function() {
+				return _this.editLabelStart(node, sync);
+			});
+			return deferred;
+		}
+						
+		//dojo.debug("editLabelStart closed, request");
+		var deferred = this.requestEditConfirmation(node, 'editLabelStart', sync);
+		
+		deferred.addCallback(function() {
+			//dojo.debug("start edit");
+			_this.doEditLabelStart(node);
+		});
+	
+		
+		return deferred;
+	
+	},
+
+	editLabelFinish: function(save, sync) {
+		var _this = this;
+		
+		var node = this.editor.node;
+		
+		var deferred = dojo.Deferred.prototype.makeCalled();
+		
+		if (!save && !node.isPhantom) {
+			deferred = this.requestEditConfirmation(this.editor.node,'editLabelFinishCancel', sync);
+		}
+		
+		if (save) {
+			if (node.isPhantom) {
+				deferred = this.sendCreateChildRequest(
+					node.parent,
+					node.getParentIndex(),
+					{title:this.editor.getContents()},
+					sync
+				);
+			} else {				
+				// this deferred has new information from server
+				deferred = this.editLabelSave(node, this.editor.getContents(), sync);
+			}
+		}
+		
+		deferred.addCallback(function(server_data) {			
+			_this.doEditLabelFinish(save, server_data);
+		});
+		
+		deferred.addErrback(function(r) {
+			//dojo.debug("Error occured");
+			//dojo.debugShallow(r);
+			_this.doEditLabelFinish(false);
+			return false;
+		});
+		
+		return deferred;
+	},
+	
+			
+	
+	/**
+	 * TODO: merge server-side info
+	 */
+	createAndEdit: function(parent, index, sync) {
+		var data = {title:parent.tree.defaultChildTitle};
+		
+		if (!this.canCreateChild(parent, index, data)) {
+			return false;
+		}
+		
+		/* close editor first */
+		if (!this.editor.isClosed()) {
+			//dojo.debug("editLabelStart editor open");
+			var deferred = this.editLabelFinish(this.editor.saveOnBlur, sync);
+			deferred.addCallback(function() {
+				return _this.createAndEdit(parent, index, sync);
+			});
+			return deferred;
+		}
+			
+		var _this = this;
+		
+		/* load parent and create child*/
+		var deferred = this.prepareCreateChild(parent, index, data, sync);
+		
+		
+		deferred.addCallback(function() {
+			var child = _this.makeDefaultNode(parent, index);			
+			child.isPhantom = true;
+			return child;
+		});
+		
+		
+		deferred.addBoth(function(r) {
+			_this.finalizeCreateChild(parent, index, data, sync);
+			return r;
+		});
+		
+		/* expand parent */
+		deferred.addCallback(function(child) {
+			var d = _this.exposeCreateChild(parent, index, data, sync);
+			d.addCallback(function() { return child });
+			return d;
+		});
+		
+		
+		deferred.addCallback(function(child) {
+			//dojo.debug("start edit");
+			_this.doEditLabelStart(child);
+			return child;
+		});
+		
+		
+		
+		return deferred;
+	
+	},
+
+	prepareDestroyChild: function(node, sync) {
+		//dojo.debug(node);
+		var deferred = this.startProcessing(node);		
+		return deferred;
+	},
+	
+	finalizeDestroyChild: function(node) {
+		this.finishProcessing(node);
+	},
+		
+
+	doDestroyChild: function(node, sync){
+
+		
+		var params = {
+			node: this.getInfo(node),
+			tree: this.getInfo(node.tree)
+		}
+
+		var deferred = this.runRpc({
+			url: this.getRpcUrl('destroyChild'),
+			sync: sync,
+			params: params			
+		});
+		
+		
+		var _this = this;
+		var args = arguments;
+		
+		deferred.addCallback(function() {			
+			dojo.widget.TreeBasicControllerV3.prototype.doDestroyChild.apply(_this,args);
+		});
+		
+						
+		return deferred;
+
+	},
+
+	// -----------------------------------------------------------------------------
+	//                             Create node stuff
+	// -----------------------------------------------------------------------------
+	sendCreateChildRequest: function(parent, index, data, sync) {
+		var params = {
+			tree: this.getInfo(parent.tree),
+			parent: this.getInfo(parent),
+			index: index,
+			data: data
+		}
+
+		var deferred = this.runRpc({
+			url: this.getRpcUrl('createChild'),
+			sync: sync,
+			params: params
+		});
+		
+		return deferred;
+	},
+		
+
+	doCreateChild: function(parent, index, data, sync){		
+		
+		if (dojo.lang.isUndefined(data.title)) {
+			data.title = parent.tree.defaultChildTitle;
+		}
+
+		var deferred = this.sendCreateChildRequest(parent,index,data,sync);
+		
+		var _this = this;
+		var args = arguments;
+		
+		
+		deferred.addCallback(function(server_data) {
+			dojo.lang.mixin(data, server_data); // add my data as less priority
+			//dojo.debug("Create ");
+			//dojo.debug(server_data);
+			return dojo.widget.TreeBasicControllerV3.prototype.doCreateChild.call(_this,parent,index,data);
+		});
+		
+						
+		return deferred;
+	},
+	
+	// TODO: merge server data into cloned node, like in createChild	
+	doClone: function(child, newParent, index, deep, sync) {
+		
+		var params = {
+			child: this.getInfo(child),
+			oldParent: this.getInfo(child.parent),
+			oldParentTree: this.getInfo(child.parent.tree),
+			newParent: this.getInfo(newParent),
+			newParentTree: this.getInfo(newParent.tree),
+			index: index,
+			deep: deep ? true : false, // undefined -> false
+			tree: this.getInfo(child.tree)
+		}
+		
+		
+		var deferred = this.runRpc({
+			url: this.getRpcUrl('clone'),
+			sync: sync,
+			params: params
+		});
+		
+		var _this = this;
+		var args = arguments;
+		
+		deferred.addCallback(function() {			
+			dojo.widget.TreeBasicControllerV3.prototype.doClone.apply(_this,args);
+		});
+		
+						
+		return deferred;	
+	}
+
+	
+});

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelector.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelector.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelector.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelector.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,179 @@
+/*
+	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.defineWidget("dojo.widget.TreeSelector", dojo.widget.HtmlWidget, function() {
+	this.eventNames = {};
+
+	this.listenedTrees = [];
+
+},
+{
+	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) {
+			if(e.ctrlKey || e.shiftKey || e.metaKey){
+				// If the node is currently selected, and they select it again while holding
+				// down a meta key, it deselects it
+				this.deselect();
+				return;
+			}
+			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: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelectorV3.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelectorV3.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelectorV3.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeSelectorV3.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,308 @@
+/*
+	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.TreeSelectorV3");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.TreeCommon");
+
+dojo.widget.defineWidget(
+	"dojo.widget.TreeSelectorV3",
+	[dojo.widget.HtmlWidget, dojo.widget.TreeCommon],
+	function() {
+		this.eventNames = {};
+		this.listenedTrees = {};
+		this.selectedNodes = [];
+		this.lastClicked = {}
+	},
+{
+	// TODO: add multiselect
+
+	listenTreeEvents: ["afterTreeCreate","afterCollapse","afterChangeTree", "afterDetach", "beforeTreeDestroy"],
+	listenNodeFilter: function(elem) { return elem instanceof dojo.widget.Widget},	
+	
+	allowedMulti: true,
+	
+	/**
+	* if time between clicks < dblselectTimeout => its dblselect
+	*/
+	dblselectTimeout: 300,
+	
+	eventNamesDefault: {
+		select : "select",
+		deselect : "deselect",
+		dblselect: "dblselect" // select already selected node.. Edit or whatever
+	},
+
+	onAfterTreeCreate: function(message) {
+		var tree = message.source;
+		dojo.event.browser.addListener(tree.domNode, "onclick", dojo.lang.hitch(this, this.onTreeClick));
+		if (dojo.render.html.ie) {
+			dojo.event.browser.addListener(tree.domNode, "ondblclick", dojo.lang.hitch(this, this.onTreeDblClick));
+		}
+		dojo.event.browser.addListener(tree.domNode, "onKey", dojo.lang.hitch(this, this.onKey));
+		
+	},
+	
+	
+	onKey: function(e) {
+		if (!e.key || e.ctrkKey || e.altKey) { return; }
+		
+		switch(e.key) {
+			case e.KEY_ENTER:
+				var node = this.domElement2TreeNode(e.target);
+				if (node) {
+					this.processNode(node, e);
+				}
+		
+		}
+	},
+	
+	
+		
+	onAfterChangeTree: function(message) {
+		
+		if (!message.oldTree && message.node.selected) {
+			this.select(message.node);
+		}
+		
+		if (!message.newTree || !this.listenedTrees[message.newTree.widgetId]) {
+			// moving from our trfee to new one that we don't listen
+			
+			if (this.selectedNode && message.node.children) {
+				this.deselectIfAncestorMatch(message.node);
+			}						
+			
+		}
+		
+		
+	},
+		
+		
+		
+	initialize: function(args) {
+
+		for(name in this.eventNamesDefault) {
+			if (dojo.lang.isUndefined(this.eventNames[name])) {
+				this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name];
+			}
+		}
+				
+	},
+
+	onBeforeTreeDestroy: function(message) {
+		this.unlistenTree(message.source);
+	},
+
+	// deselect node if ancestor is collapsed
+	onAfterCollapse: function(message) {		
+		this.deselectIfAncestorMatch(message.source);		
+	},
+
+	// IE will throw select -> dblselect. Need to transform to select->select
+	onTreeDblClick: function(event) {
+		this.onTreeClick(event);			
+	},		
+		
+	checkSpecialEvent: function(event) {		
+		return event.shiftKey || event.ctrlKey;
+	},
+	
+	
+	onTreeClick: function(event) {		
+		
+		var node = this.domElement2TreeNode(event.target);
+				
+		if (!node) {
+			return;
+		}
+		
+		
+		
+		var checkLabelClick = function(domElement) {
+			return domElement === node.labelNode;
+		}
+		
+		if (this.checkPathCondition(event.target, checkLabelClick)) {
+			this.processNode(node, event);			
+		}
+		
+		
+	},
+	
+	
+	/**
+	 * press on selected with ctrl => deselect it
+	 * press on selected w/o ctrl => dblselect it and deselect all other
+	 *
+	 * press on unselected with ctrl => add it to selection
+	 *
+	 * event may be both mouse & keyboard enter
+	 */
+	processNode: function(node, event) {
+		
+		if (node.actionIsDisabled(node.actions.SELECT)) {
+			return;
+		}
+		
+		//dojo.debug("click "+node+ "special "+this.checkSpecialEvent(event));		
+		
+		if (dojo.lang.inArray(this.selectedNodes, node)) {
+				
+			if(this.checkSpecialEvent(event)){				
+				// If the node is currently selected, and they select it again while holding
+				// down a meta key, it deselects it
+				this.deselect(node);
+				return;
+			}
+			
+			var _this = this;
+			var i=0;
+			var selectedNode;
+			
+			/* remove all nodes from selection excepts this one */
+			while(this.selectedNodes.length > i) {
+				selectedNode = this.selectedNodes[i];
+				if (selectedNode !== node) {
+					//dojo.debug("Deselect "+selectedNode);
+					this.deselect(selectedNode);
+					continue;
+				}
+			
+				i++; // skip the doubleclicked node
+			}
+		
+			/* lastClicked.node may be undefined if node was selected(before) programmatically */
+			var wasJustClicked = this.checkRecentClick(node)
+			
+			eventName = wasJustClicked ? this.eventNames.dblselect : this.eventNames.select;
+			if (wasJustClicked) {
+				eventName = this.eventNames.dblselect;
+				/* after dblselect, next select is usual select */
+				this.forgetLastClicked();
+			} else {
+				eventName = this.eventNames.select;
+				this.setLastClicked(node)
+			}
+			
+			dojo.event.topic.publish(eventName, { node: node });
+			
+			return;
+		}
+		
+		/* if unselected node..	*/
+		
+		this.deselectIfNoMulti(event);
+		
+		//dojo.debug("select");
+		
+		this.setLastClicked(node);
+		
+		this.select(node);
+
+	},
+	
+	forgetLastClicked: function() {
+		this.lastClicked = {}
+	},
+	
+	setLastClicked: function(node) {
+		this.lastClicked.date = new Date();	
+		this.lastClicked.node = node;
+	},
+	
+	checkRecentClick: function(node) {
+		var diff = new Date() - this.lastClicked.date;
+		//dojo.debug(new Date())
+		//dojo.debug("check old "+this.lastClicked.node+" now "+(new Date())+" diff "+diff)
+		if (this.lastClicked.node && diff < this.dblselectTimeout) {
+			return true;
+		} else {
+			return false;
+		}
+	},
+	
+	// deselect all if no meta key or disallowed
+	deselectIfNoMulti: function(event) {
+		if (!this.checkSpecialEvent(event) || !this.allowedMulti) {
+			//dojo.debug("deselect All");
+			this.deselectAll();
+		}
+	},
+
+	deselectIfAncestorMatch: function(ancestor) {
+		/* deselect all nodes with this ancestor */
+		var _this = this;
+		dojo.lang.forEach(this.selectedNodes, function(node) {
+			var selectedNode = node;
+			node = node.parent
+			while (node && node.isTreeNode) {
+				//dojo.debug("ancestor try "+node);
+				
+				if (node === ancestor) {
+					_this.deselect(selectedNode); 
+					return;					
+				}
+				node = node.parent;
+			}
+		});
+	},
+	
+			
+
+
+	onAfterDetach: function(message) {
+		this.deselectIfAncestorMatch(message.child);		
+	},
+
+
+	select: function(node) {
+
+		var index = dojo.lang.find(this.selectedNodes, node, true);
+		
+		if (index >=0 ) {
+			return; // already selected
+		}
+				
+		//dojo.debug("select "+node);
+		this.selectedNodes.push(node);
+						
+		dojo.event.topic.publish(this.eventNames.select, {node: node} );
+	},
+
+
+	deselect: function(node){
+		var index = dojo.lang.find(this.selectedNodes, node, true);
+		if (index < 0) {
+			//dojo.debug("not selected");
+			return; // not selected
+		}
+		
+		//dojo.debug("deselect "+node);
+		//dojo.debug((new Error()).stack);
+		
+		this.selectedNodes.splice(index, 1);
+		dojo.event.topic.publish(this.eventNames.deselect, {node: node} );
+		//dojo.debug("deselect");
+
+	},
+	
+	deselectAll: function() {
+		//dojo.debug("deselect all "+this.selectedNodes);
+		while (this.selectedNodes.length) {
+			this.deselect(this.selectedNodes[0]);
+		}
+	}
+
+});
+
+
+

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeTimeoutIterator.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeTimeoutIterator.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeTimeoutIterator.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeTimeoutIterator.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,169 @@
+/*
+	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.TreeTimeoutIterator");
+
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.json")
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.TreeCommon");
+
+
+/**
+ * Iterates the tree processNext
+ * filterFunc/filterObj called to determine if I need to pass the node
+ * 
+ * callFunc/callObj called to process the node
+ *    callObj.callFunc(elem, iterator) should call iterator.forward() to go on
+ *    callFunc may change elem to another object (e.g create widget from it),
+ *       keeping its parent and parent position are untouched *
+ *
+ * finishFunc/finishObj called at the end
+ *
+ * TODO: it should work only sync-way to solve CPU-hungry tasks 
+ */
+ dojo.declare(
+ 	"dojo.widget.TreeTimeoutIterator",
+ 	null,
+ 	
+function(elem, callFunc, callObj) {
+	var _this = this;
+	
+	this.currentParent = elem;
+	
+	this.callFunc = callFunc;
+	this.callObj = callObj ? callObj: this;
+	this.stack = [];	
+},
+
+{
+	// public
+	maxStackDepth: Number.POSITIVE_INFINITY,
+	
+	stack: null,
+	currentParent: null,
+		
+	currentIndex: 0,
+	
+	filterFunc: function() { return true },
+	
+	finishFunc: function() { return true },
+	
+	
+	setFilter: function(func, obj) {
+		this.filterFunc = func;
+		this.filterObj = obj;
+	},
+	
+	
+	setMaxLevel: function(level) {
+		this.maxStackDepth = level-2;
+	},
+	
+	forward: function(timeout) {
+		var _this = this;
+		
+		if (this.timeout) { // if timeout required between forwards
+			// tid will be assigned at the end of outer func execution
+			var tid = setTimeout(function() {_this.processNext(); clearTimeout(tid); }, _this.timeout);
+		} else {
+			return this.processNext();
+		}
+	},
+	
+	start: function(processFirst) {
+		if (processFirst) {			
+			return this.callFunc.call(this.callObj, this.currentParent, this);			
+		}
+				
+		return this.processNext();
+	},
+	
+	/**
+	 * @private
+	 * find next node, move current parent to it if possible & process
+	 */
+	processNext: function() {
+				
+		//dojo.debug("processNext with currentParent "+this.currentParent+" index "+this.currentIndex);
+		var handler;
+		
+		var _this = this;
+		
+		var found;
+		
+		var next;
+			
+		if (this.maxStackDepth == -2) {   
+			return; // process only first cause level=0, do not process children
+		}
+		
+		while (true) {
+			var children = this.currentParent.children;
+		
+			if (children && children.length) {
+		
+				// look for a node that can be the next target
+				do {					
+					next = children[this.currentIndex];
+					//dojo.debug("check "+next);
+				} while (this.currentIndex++ < children.length && !(found = this.filterFunc.call(this.filterObj,next)));
+			
+			
+				if (found) {
+					//dojo.debug("found "+next);
+					// move to next node as new parent if depth is fine
+					// I can't check current children to decide whether to move it or not,
+					// because expand may populate children					
+					if (next.isFolder && this.stack.length <= this.maxStackDepth) {
+						this.moveParent(next,0);
+					}
+					//dojo.debug("Run callFunc on "+next);
+					return this.callFunc.call(this.callObj, next, this);					
+				}
+			}
+				
+			if (this.stack.length) {
+				this.popParent();
+				continue;
+			}
+			
+			break;
+		}
+
+		/**
+		 * couldn't find next node to process, finish here
+		 */
+		return this.finishFunc.call(this.finishObj);
+
+	},
+	
+	setFinish: function(func, obj) {
+		this.finishFunc = func;
+		this.finishObj = obj;
+	},
+		
+	popParent: function() {
+		var p = this.stack.pop();
+		//dojo.debug("Pop "+p[0]+":"+p[1]);		
+		this.currentParent = p[0];
+		this.currentIndex = p[1];
+	},
+	
+	moveParent: function(nextParent, nextIndex) {
+		//dojo.debug("Move from "+this.currentParent+":"+this.currentIndex+" to "+nextParent+":"+nextIndex);
+		this.stack.push([this.currentParent, this.currentIndex]);
+		this.currentParent = nextParent;
+		this.currentIndex = nextIndex;
+	}
+
+});
\ No newline at end of file

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeToggleOnSelect.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeToggleOnSelect.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeToggleOnSelect.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeToggleOnSelect.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,46 @@
+/*
+	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.TreeToggleOnSelect");
+
+dojo.require("dojo.widget.HtmlWidget");
+
+
+/**
+ * when a node is selected, expands tree to make it visible
+ * useful for program expansion
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.TreeToggleOnSelect",
+	dojo.widget.HtmlWidget,
+{
+	selector: "",
+	controller: "",
+	selectEvent: "select",	
+	
+	initialize: function() {
+		this.selector = dojo.widget.byId(this.selector);
+		this.controller = dojo.widget.byId(this.controller);
+		
+		dojo.event.topic.subscribe(this.selector.eventNames[this.selectEvent], this, "onSelectEvent");	
+	},
+
+	
+	onSelectEvent: function(message) {
+	//	if (this.selectEvent
+		var node = message.node
+		node.isExpanded ? this.controller.collapse(node) : this.controller.expand(node)	
+	}
+	
+	
+
+});
+

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeV3.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeV3.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeV3.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeV3.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,347 @@
+/*
+	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
+*/
+
+/**
+ * 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
+*/
+
+/**
+ * TODO: use domNode.cloneNode instead of createElement for grid
+ * Should be faster (lyxsus)
+ */
+dojo.provide("dojo.widget.TreeV3");
+
+dojo.require("dojo.widget.TreeWithNode");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.TreeNodeV3");
+
+dojo.widget.defineWidget(
+	"dojo.widget.TreeV3",
+	[dojo.widget.HtmlWidget, dojo.widget.TreeWithNode],
+	function() {
+		this.eventNames = {};
+		
+		this.DndAcceptTypes = [];
+		this.actionsDisabled = [];
+		
+		this.listeners = [];
+		
+		this.tree = this;
+	},
+{
+	DndMode: "",
+
+	/**
+	 * factory to generate default widgets
+	 */
+	defaultChildWidget: null,
+	
+	defaultChildTitle: "New Node", // for editing
+	
+	
+	eagerWidgetInstantiation: false,
+	
+	eventNamesDefault: {
+
+		// tree created.. Perform tree-wide actions if needed
+		afterTreeCreate: "afterTreeCreate",
+		beforeTreeDestroy: "beforeTreeDestroy",
+		/* can't name it "beforeDestroy", because such name causes memleaks in IE */
+		beforeNodeDestroy: "beforeNodeDestroy",
+		afterChangeTree: "afterChangeTree",
+
+		afterSetFolder: "afterSetFolder",
+		afterUnsetFolder: "afterUnsetFolder",		
+		beforeMoveFrom: "beforeMoveFrom",
+		beforeMoveTo: "beforeMoveTo",
+		afterMoveFrom: "afterMoveFrom",
+		afterMoveTo: "afterMoveTo",
+		afterAddChild: "afterAddChild",
+		afterDetach: "afterDetach",
+		afterExpand: "afterExpand",
+		beforeExpand: "beforeExpand",
+		afterSetTitle: "afterSetTitle",		
+		afterCollapse: "afterCollapse",	
+		beforeCollapse: "beforeCollapse"
+	},
+
+	classPrefix: "Tree",
+	
+	style: "",
+	
+	/**
+	 * is it possible to add a new child to leaf ?
+	 */	
+	allowAddChildToLeaf: true,
+	
+	/**
+	 * when last children is removed from node should it stop being a "folder" ?
+	 */
+	unsetFolderOnEmpty: true,
+
+
+	DndModes: {
+		BETWEEN: 1,
+		ONTO: 2
+	},
+
+	DndAcceptTypes: "",
+
+    // will have cssRoot before it 
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/TreeV3.css"),
+
+	templateString: '<div style="${this.style}">\n</div>',
+
+	isExpanded: true, // consider this "root node" to be always expanded
+
+	isTree: true,
+	
+	
+
+	createNode: function(data) {
+			
+		data.tree = this.widgetId;		
+		
+		if (data.widgetName) {
+			// TODO: check if such widget has createSimple			
+			return dojo.widget.createWidget(data.widgetName, data);		
+		} else if (this.defaultChildWidget.prototype.createSimple) {			
+			return this.defaultChildWidget.prototype.createSimple(data);					
+		} else {
+			var ns = this.defaultChildWidget.prototype.ns; 
+			var wt = this.defaultChildWidget.prototype.widgetType; 
+
+			return dojo.widget.createWidget(ns + ":" + wt, data); 
+		}
+ 	    	
+	},
+				
+
+	// expandNode has +- CSS background. Not img.src for performance, background src string resides in single place.
+	// selection in KHTML/Mozilla disabled treewide, IE requires unselectable for every node
+	// you can add unselectable if you want both in postCreate of tree and in this template
+
+	// create new template and put into prototype
+	makeNodeTemplate: function() {
+		
+		var domNode = document.createElement("div");
+		dojo.html.setClass(domNode, this.classPrefix+"Node "+this.classPrefix+"ExpandLeaf "+this.classPrefix+"ChildrenNo");		
+		this.nodeTemplate = domNode;
+		
+		var expandNode = document.createElement("div");
+		var clazz = this.classPrefix+"Expand";
+		if (dojo.render.html.ie) {
+			clazz = clazz + ' ' + this.classPrefix+"IEExpand";
+		}
+		dojo.html.setClass(expandNode, clazz);
+		
+		this.expandNodeTemplate = expandNode;
+
+		// need <span> inside <div>
+		// div for multiline support, span for styling exactly the text, not whole line
+		var labelNode = document.createElement("span");
+		dojo.html.setClass(labelNode, this.classPrefix+"Label");
+		this.labelNodeTemplate = labelNode;
+		
+		var contentNode = document.createElement("div");
+		var clazz = this.classPrefix+"Content";
+		
+		/**
+		 * IE<7 does not support min-height properly so I have to rely
+		 * on this hack
+		 * FIXME: do it in CSS only
+		 */
+		if (dojo.render.html.ie && !dojo.render.html.ie70) {
+			clazz = clazz + ' ' + this.classPrefix+"IEContent";
+		}	
+		
+				
+		dojo.html.setClass(contentNode, clazz);
+		
+		this.contentNodeTemplate = contentNode;
+		
+		domNode.appendChild(expandNode);
+		domNode.appendChild(contentNode);
+		contentNode.appendChild(labelNode);
+		
+		
+	},
+
+	makeContainerNodeTemplate: function() {
+		
+		var div = document.createElement('div');
+		div.style.display = 'none';			
+		dojo.html.setClass(div, this.classPrefix+"Container");
+		
+		this.containerNodeTemplate = div;
+		
+	},
+
+	
+	actions: {
+    	ADDCHILD: "ADDCHILD"
+	},
+
+
+	getInfo: function() {
+		var info = {
+			widgetId: this.widgetId,
+			objectId: this.objectId
+		}
+
+		return info;
+	},
+
+	adjustEventNames: function() {
+		
+		for(var name in this.eventNamesDefault) {
+			if (dojo.lang.isUndefined(this.eventNames[name])) {
+				this.eventNames[name] = this.widgetId+"/"+this.eventNamesDefault[name];
+			}
+		}
+	},
+
+	
+	adjustDndMode: function() {
+		var _this = this;
+		
+		
+		var DndMode = 0;
+		dojo.lang.forEach(this.DndMode.split(';'),
+			function(elem) {
+				var mode = _this.DndModes[dojo.string.trim(elem).toUpperCase()];
+				if (mode) DndMode = DndMode | mode;
+			}
+		 );
+	
+		
+		this.DndMode = DndMode;
+
+	},
+	
+	/**
+	 * publish destruction event so that any listeners should stop listening
+	 */
+	destroy: function() {
+		dojo.event.topic.publish(this.tree.eventNames.beforeTreeDestroy, { source: this } );
+
+		return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments);
+	},
+
+	initialize: function(args){
+		
+		this.domNode.widgetId = this.widgetId;
+		
+		for(var i=0; i<this.actionsDisabled.length;i++) {
+			this.actionsDisabled[i] = this.actionsDisabled[i].toUpperCase();
+		}
+		
+		//dojo.debug(args.defaultChildWidget ? true : false)
+		
+		if (!args.defaultChildWidget) {
+			this.defaultChildWidget = dojo.widget.TreeNodeV3;
+		} else {
+			this.defaultChildWidget = dojo.lang.getObjPathValue(args.defaultChildWidget);
+		}
+		
+		this.adjustEventNames();
+		this.adjustDndMode();
+
+		this.makeNodeTemplate();
+		this.makeContainerNodeTemplate();
+		
+		this.containerNode = this.domNode;
+		
+		dojo.html.setClass(this.domNode, this.classPrefix+"Container");
+		
+		var _this = this;
+			
+		//dojo.html.disableSelection(this.domNode)
+				
+		dojo.lang.forEach(this.listeners,
+			function(elem) {
+				var t = dojo.lang.isString(elem) ? dojo.widget.byId(elem) : elem;
+				t.listenTree(_this)				
+			}
+		);
+		
+
+		
+		
+
+	},
+
+	
+	postCreate: function() {						
+		dojo.event.topic.publish(this.eventNames.afterTreeCreate, { source: this } );
+	},
+	
+	
+	/**
+	 * 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) {
+		
+		if (!child.parent) {
+			dojo.raise(this.widgetType+": child can be moved only while it's attached");
+		}
+		
+		var oldParent = child.parent;
+		var oldTree = child.tree;
+		var oldIndex = child.getParentIndex();
+		var newTree = newParent.tree;
+		var newParent = newParent;
+		var newIndex = index;
+
+		var message = {
+				oldParent: oldParent, oldTree: oldTree, oldIndex: oldIndex,
+				newParent: newParent, newTree: newTree, newIndex: newIndex,
+				child: child
+		};
+
+		dojo.event.topic.publish(oldTree.eventNames.beforeMoveFrom, message);
+		dojo.event.topic.publish(newTree.eventNames.beforeMoveTo, message);
+		
+		this.doMove.apply(this, arguments);
+
+		
+		/* publish events here about structural changes for both source and target trees */
+		dojo.event.topic.publish(oldTree.eventNames.afterMoveFrom, message);
+		dojo.event.topic.publish(newTree.eventNames.afterMoveTo, message);
+
+	},
+
+
+	/* do actual parent change here. Write remove child first */
+	doMove: function(child, newParent, index) {
+		//dojo.debug("MOVE "+child+" to "+newParent+" at "+index);
+
+		//var parent = child.parent;
+		child.doDetach();
+
+		//dojo.debug("addChild "+child+" to "+newParent+" at "+index);
+
+		newParent.doAddChild(child, index);
+	},
+
+	toString: function() {
+		return "["+this.widgetType+" ID:"+this.widgetId	+"]"
+	}
+
+});

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeWithNode.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeWithNode.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeWithNode.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/TreeWithNode.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,277 @@
+/*
+	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.lang.declare");
+dojo.provide("dojo.widget.TreeWithNode");
+
+dojo.declare(
+	"dojo.widget.TreeWithNode",
+	null,
+	function(){ },
+{
+	/*
+	 * dynamic loading-related stuff. 
+	 * When an empty folder node appears, it is "UNCHECKED" first,
+	 * then after Rpc call it becomes LOADING and, finally LOADED
+	 *
+	 * tree may be dynamically loaded also
+	 */
+	loadStates: {
+		UNCHECKED: "UNCHECKED",
+    	LOADING: "LOADING",
+    	LOADED: "LOADED"
+	},
+	
+	state: "UNCHECKED",  // after creation will change to loadStates: "loaded/loading/unchecked"
+
+    //RpcUrl: "", // user can override rpc url for specific nodes
+
+	objectId: "", // the widget represents an object
+
+
+	// I need this to parse children
+	isContainer: true,
+	
+	lockLevel: 0, // lock ++ unlock --, so nested locking works fine
+	
+	lock: function() {
+		this.lockLevel++;
+	},
+	unlock: function() {
+		if (!this.lockLevel) {
+			//dojo.debug((new Error()).stack);
+			dojo.raise(this.widgetType+" unlock: not locked");
+		}
+		this.lockLevel--;
+	},
+	
+	
+	expandLevel: 0, // expand to level automatically
+	loadLevel: 0, // load to level automatically
+		
+	hasLock: function() {
+		return this.lockLevel>0;
+	},
+
+	isLocked: function() {
+		var node = this;
+		while (true) {
+			if (node.lockLevel) {
+				return true;
+			}
+			if (!node.parent || node.isTree) {
+				break;
+			}
+			
+			node = node.parent;
+			
+		}
+
+		return false;
+	},
+
+	
+	flushLock: function() {
+		this.lockLevel = 0;
+		//this.unMarkLoading();
+	},
+	
+	
+	actionIsDisabled: function(action) {
+		var disabled = false;
+
+		if (dojo.lang.inArray(this.actionsDisabled, action)) {
+			disabled = true;
+		}
+
+
+		//dojo.debug("Check "+this+" "+disabled)
+		
+		
+		if (this.isTreeNode) {
+			if (!this.tree.allowAddChildToLeaf && action == this.actions.ADDCHILD && !this.isFolder) {
+				disabled = true;
+			}
+		}
+		return disabled;
+	},
+		
+	actionIsDisabledNow: function(action) {
+		return this.actionIsDisabled(action) || this.isLocked();
+	},
+	
+	
+	/**
+	 * childrenArray is array of Widgets or array of Objects
+	 * widgets may be both attached and detached
+	 *
+	 * Use Cases
+	 * 1) lots of widgets are packed and passed in.
+	 *  - widgets are created
+	 *  - widgets have no parent (detached or not attached yet)
+	 *
+	 * 2) array of widgets and data objects passed in with flag makeWidgetsFromChildren
+	 *  - some widgets are not created
+	 *  - all objects have no parent
+	 *
+	 * 3) expand is called with makeWidgetsFromChildren=true
+	 *  - some objects need to be turned into widgets
+	 *  - some widgets have parent (e.g markup), some widgets and objects do not
+	 *
+	 *  Will folderize a node as side-effect.
+	 */
+	setChildren: function(childrenArray) {
+		//dojo.profile.start("setChildren "+this);
+		//dojo.debug("setChildren in "+this);
+		
+		
+		if (this.isTreeNode && !this.isFolder) {
+			//dojo.debug("folder parent "+parent+ " isfolder "+parent.isFolder);
+			this.setFolder();
+		} else if (this.isTreeNode) {
+			this.state = this.loadStates.LOADED;
+		}
+		
+		var hadChildren = this.children.length > 0;
+		
+        if (hadChildren && childrenArray){
+            // perf: most of time setChildren used for empty nodes, so save function call
+            this.destroyChildren()
+        }
+        
+		if (childrenArray) {
+			this.children = childrenArray;
+		}
+		
+
+
+		var hasChildren = this.children.length > 0;
+		if (this.isTreeNode && hasChildren != hadChildren) {
+			// call only when hasChildren state changes
+			this.viewSetHasChildren();
+		}
+		
+
+
+		for(var i=0; i<this.children.length; i++) {
+			var child = this.children[i];
+			
+			//dojo.profile.start("setChildren - create "+this);
+			
+			if (!(child instanceof dojo.widget.Widget)) {
+				
+				child = this.children[i] = this.tree.createNode(child);
+				var childWidgetCreated = true;	
+				//dojo.debugShallow(child)
+				
+				//dojo.debug("setChildren creates node "+child);
+			} else {
+				var childWidgetCreated = false;
+			}
+			
+			//dojo.profile.end("setChildren - create "+this);
+
+			//dojo.profile.start("setChildren - attach "+this);
+
+			if (!child.parent) { // detached child
+				
+				//dojo.debug("detached child "+child);
+				
+				child.parent = this;
+
+				//dojo.profile.start("setChildren - updateTree "+this);
+				
+				if (this.tree !== child.tree) {				
+					child.updateTree(this.tree);
+				}
+				//dojo.profile.end("setChildren - updateTree "+this);
+
+			
+				//dojo.debug("Add layout for "+child);
+				child.viewAddLayout();
+				this.containerNode.appendChild(child.domNode);
+					
+				var message = {
+					child: child,
+					index: i,
+					parent: this,
+					childWidgetCreated: childWidgetCreated
+				}
+			
+				delete dojo.widget.manager.topWidgets[child.widgetId];
+		
+
+				//dojo.profile.start("setChildren - event "+this);
+				//dojo.debug("publish "+this.tree.eventNames.afterAddChild)
+				dojo.event.topic.publish(this.tree.eventNames.afterAddChild, message);
+
+				//dojo.profile.end("setChildren - event "+this);
+
+			}
+			
+			if (this.tree.eagerWidgetInstantiation) {
+				dojo.lang.forEach(this.children, function(child) {
+					child.setChildren();
+				});
+			}
+
+			//dojo.profile.end("setChildren - attach "+this);
+
+		
+		}
+		
+
+
+		//dojo.profile.end("setChildren "+this);
+		
+	},	
+	
+	
+	doAddChild: function(child, index) {
+		return this.addChild(child, index, true);
+	},
+		
+	addChild: function(child, index, dontPublishEvent) {
+		if (dojo.lang.isUndefined(index)) {
+			index = this.children.length;
+		}
+		
+		//dojo.debug("doAddChild "+index+" called for "+this+" child "+child+" existing children "+(this.children.length ? this.children : "<no children>"));
+				
+		if (!child.isTreeNode){
+			dojo.raise("You can only add TreeNode widgets to a "+this.widgetType+" widget!");
+			return;
+		}
+			
+		this.children.splice(index, 0, child);
+		child.parent = this;
+				
+		child.addedTo(this, index, dontPublishEvent);
+		
+		// taken from DomWidget.registerChild
+		// delete from widget list that are notified on resize etc (no parent)
+		delete dojo.widget.manager.topWidgets[child.widgetId];
+				
+	},
+	
+	 /**
+     * does not inform children about resize (skips onShow),
+     * because on large trees that's slow
+     */
+    onShow: function() {        
+        this.animationInProgress=false;
+    },
+    
+    onHide: function() {        
+        this.animationInProgress=false;
+    }
+	
+});

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/UsTextbox.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/UsTextbox.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/UsTextbox.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/UsTextbox.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,93 @@
+/*
+	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.UsTextbox");
+
+dojo.require("dojo.widget.ValidationTextbox");
+dojo.require("dojo.validate.us");
+
+dojo.widget.defineWidget(
+	"dojo.widget.UsStateTextbox",
+	dojo.widget.ValidationTextbox,
+	{
+		// summary:
+		//		a Textbox which tests for a United States state abbreviation
+
+		// allowTerritories: Boolean
+		//		  Allow Guam, Puerto Rico, etc.  Default is true.
+
+		// allowMilitary: Boolean
+		//     Allow military 'states', e.g. Armed Forces Europe (AE). Default is true.
+
+		mixInProperties: function(/*Object*/localProperties){
+			// summary: see dojo.widget.Widget
+
+			// Initialize properties in super-class.
+			dojo.widget.UsStateTextbox.superclass.mixInProperties.apply(this, arguments);
+
+			// Get properties from markup attributes, and assign to flags object.
+			if(localProperties.allowterritories){
+				this.flags.allowTerritories = (localProperties.allowterritories == "true");
+			}
+			if(localProperties.allowmilitary){
+				this.flags.allowMilitary = (localProperties.allowmilitary == "true");
+			}
+		},
+
+		isValid: function(){
+			// summary: see dojo.widget.ValidationTextbox
+			return dojo.validate.us.isState(this.textbox.value, this.flags);
+		}
+	}
+);
+
+/*
+  ****** UsZipTextbox ******
+
+  A subclass of ValidationTextbox.
+  Over-rides isValid to test if input is a US zip code.
+  Validates zip-5 and zip-5 plus 4.
+*/
+dojo.widget.defineWidget(
+	"dojo.widget.UsZipTextbox",
+	dojo.widget.ValidationTextbox,
+	{
+		// summary: a Textbox which tests for a United States postal code
+		isValid: function(){
+			// summary: see dojo.widget.ValidationTextbox
+			return dojo.validate.us.isZipCode(this.textbox.value);
+		}
+	}
+);
+
+dojo.widget.defineWidget(
+	"dojo.widget.UsSocialSecurityNumberTextbox",
+	dojo.widget.ValidationTextbox,
+	{
+		// summary: a Textbox which tests for a United States Social Security number
+		isValid: function(){
+			// summary: see dojo.widget.ValidationTextbox
+			return dojo.validate.us.isSocialSecurityNumber(this.textbox.value);
+		}
+	}
+);
+
+dojo.widget.defineWidget(
+	"dojo.widget.UsPhoneNumberTextbox",
+	dojo.widget.ValidationTextbox,
+	{
+		// summary: a Textbox which tests for a United States 10-digit telephone number, extension is optional.
+
+		isValid: function(){
+			// summary: see dojo.widget.ValidationTextbox
+			return dojo.validate.us.isPhoneNumber(this.textbox.value);
+		}
+	}
+);

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/ValidationTextbox.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/ValidationTextbox.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/ValidationTextbox.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/ValidationTextbox.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,204 @@
+/*
+	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.ValidationTextbox");
+
+dojo.require("dojo.widget.Textbox");
+dojo.require("dojo.i18n.common");
+
+dojo.widget.defineWidget(
+	"dojo.widget.ValidationTextbox",
+	dojo.widget.Textbox,
+	function() {
+		// summary:
+		//		A subclass of Textbox.
+		//		Over-ride isValid in subclasses to perform specific kinds of validation.
+		
+		// this property isn't a primitive and needs to be created on a per-item basis.
+		this.flags = {};
+	},
+	{
+		// default values for new subclass properties
+		// required: Boolean
+		//		Can be true or false, default is false.
+		required: false,
+		rangeClass: "range",
+		// invalidClass: String
+		//		Class used to format displayed text in page if necessary to override default class
+		invalidClass: "invalid",
+		// missingClass: String
+		//		Override default class used for missing input data
+		missingClass: "missing",
+		classPrefix: "dojoValidate",
+		// size: String
+		//		Basic input tag size declaration.
+		size: "",
+		// maxlength: String
+		//		Basic input tag maxlength declaration.	
+		maxlength: "",
+		// promptMessage: String
+		//		Will not issue invalid message if field is populated with default user-prompt text
+		promptMessage: "",
+		// invalidMessage: String
+		// 		The message to display if value is invalid.
+		invalidMessage: "",
+		// missingMessage: String
+		//		The message to display if value is missing.
+		missingMessage: "",
+		rangeMessage: "",
+		// listenOnKeyPress: Boolean
+		//		Updates messages on each key press.  Default is true.
+		listenOnKeyPress: true,
+		htmlfloat: "none",
+		lastCheckedValue: null,
+	
+		templatePath: dojo.uri.dojoUri("src/widget/templates/ValidationTextbox.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/Validate.css"),
+		
+		// new DOM nodes
+		invalidSpan: null,
+		missingSpan: null,
+		rangeSpan: null,
+
+		getValue: function() {
+			return this.textbox.value;
+		},
+	
+		setValue: function(value) {
+			this.textbox.value = value;
+			this.update();
+		},
+	
+		isValid: function() {
+			// summary: Need to over-ride with your own validation code in subclasses
+			return true;
+		},
+	
+		isInRange: function() {
+			// summary: Need to over-ride with your own validation code in subclasses
+			return true;
+		},
+	
+		isEmpty: function() {
+			// summary: Checks for whitespace
+			return ( /^\s*$/.test(this.textbox.value) ); // Boolean
+		},
+	
+		isMissing: function() {
+			// summary: Checks to see if value is required and is whitespace
+			return ( this.required && this.isEmpty() ); // Boolean
+		},
+	
+		update: function() {
+			// summary:
+			//		Called by oninit, onblur, and onkeypress.
+			// description:
+			//		Show missing or invalid messages if appropriate, and highlight textbox field.
+			this.lastCheckedValue = this.textbox.value;
+			this.missingSpan.style.display = "none";
+			this.invalidSpan.style.display = "none";
+			this.rangeSpan.style.display = "none";
+	
+			var empty = this.isEmpty();
+			var valid = true;
+			if(this.promptMessage != this.textbox.value){ 
+				valid = this.isValid(); 
+			}
+			var missing = this.isMissing();
+	
+			// Display at most one error message
+			if(missing){
+				this.missingSpan.style.display = "";
+			}else if( !empty && !valid ){
+				this.invalidSpan.style.display = "";
+			}else if( !empty && !this.isInRange() ){
+				this.rangeSpan.style.display = "";
+			}
+			this.highlight();
+		},
+		
+		updateClass: function(className){
+			// summary: used to ensure that only 1 validation class is set at a time
+			var pre = this.classPrefix;
+			dojo.html.removeClass(this.textbox,pre+"Empty");
+			dojo.html.removeClass(this.textbox,pre+"Valid");
+			dojo.html.removeClass(this.textbox,pre+"Invalid");
+			dojo.html.addClass(this.textbox,pre+className);
+		},
+		
+		highlight: function() {
+			// summary: by Called oninit, and onblur.
+			
+			// highlight textbox background 
+			if (this.isEmpty()) {
+				this.updateClass("Empty");
+			}else if (this.isValid() && this.isInRange() ){
+				this.updateClass("Valid");
+			}else if(this.textbox.value != this.promptMessage){ 
+				this.updateClass("Invalid");
+			}else{
+				this.updateClass("Empty");
+			}
+		},
+	
+		onfocus: function(evt) {
+			if ( !this.listenOnKeyPress) {
+				this.updateClass("Empty");
+//			    this.textbox.style.backgroundColor = "";
+			}
+		},
+	
+		onblur: function(evt) { 
+			this.filter();
+			this.update(); 
+		},
+	
+		onkeyup: function(evt){ 
+			if(this.listenOnKeyPress){ 
+				//this.filter();  trim is problem if you have to type two words
+				this.update(); 
+			}else if (this.textbox.value != this.lastCheckedValue){
+				this.updateClass("Empty");
+//			    this.textbox.style.backgroundColor = "";
+			}
+		},
+
+		postMixInProperties: function(localProperties, frag) {
+			dojo.widget.ValidationTextbox.superclass.postMixInProperties.apply(this, arguments);
+			this.messages = dojo.i18n.getLocalization("dojo.widget", "validate", this.lang);
+			dojo.lang.forEach(["invalidMessage", "missingMessage", "rangeMessage"], function(prop) {
+				if(this[prop]){ this.messages[prop] = this[prop]; }
+			}, this);
+		},
+	
+		fillInTemplate: function() {
+			dojo.widget.ValidationTextbox.superclass.fillInTemplate.apply(this, arguments);
+
+			// Attach isMissing and isValid methods to the textbox.
+			// We may use them later in connection with a submit button widget.
+			// TODO: this is unorthodox; it seems better to do it another way -- Bill
+			this.textbox.isValid = function() { this.isValid.call(this); };
+			this.textbox.isMissing = function() { this.isMissing.call(this); };
+			this.textbox.isInRange = function() { this.isInRange.call(this); };
+			dojo.html.setClass(this.invalidSpan,this.invalidClass);
+			this.update(); 
+			
+			// apply any filters to initial value
+			this.filter();
+
+			// set table to be inlined (technique varies by browser)
+			// TODO: use method in dojo.html that does this
+			if(dojo.render.html.ie){ dojo.html.addClass(this.domNode, "ie"); }
+			if(dojo.render.html.moz){ dojo.html.addClass(this.domNode, "moz"); }
+			if(dojo.render.html.opera){ dojo.html.addClass(this.domNode, "opera"); }
+			if(dojo.render.html.safari){ dojo.html.addClass(this.domNode, "safari"); }
+		}
+	}
+);

Added: myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Widget.js
URL: http://svn.apache.org/viewvc/myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Widget.js?view=auto&rev=495409
==============================================================================
--- myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Widget.js (added)
+++ myfaces/tomahawk/trunk/core/src/main/resources/org/apache/myfaces/custom/dojo/resource/src/widget/Widget.js Thu Jan 11 14:35:53 2007
@@ -0,0 +1,761 @@
+/*
+	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.require("dojo.lang.func");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.extras");
+dojo.require("dojo.lang.declare");
+dojo.require("dojo.ns");
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.event.*");
+dojo.require("dojo.a11y");
+
+dojo.declare("dojo.widget.Widget", null,
+	function(){
+		// these properties aren't primitives and need to be created on a per-item
+		// basis.
+
+		// children: Array
+		//		a list of all of the widgets that have been added as children of
+		//		this component. Should only have values if isContainer is true.
+		this.children = [];
+
+		// extraArgs: Object
+		//		a map of properties which the widget system tried to assign from
+		//		user input but did not correspond to any of the properties set on
+		//		the class prototype. These names will also be available in all
+		//		lower-case form in this map
+		this.extraArgs = {};
+	},
+{
+	// parent: Widget
+	//		the parent of this widget
+	parent: null, 
+
+	// isTopLevel: Boolean
+	//		should this widget eat all events that bubble up to it?
+	//		obviously, top-level and modal widgets should set these appropriately
+	isTopLevel:  false, 
+
+	// disabled: Boolean
+	//		should this widget respond to user input?
+	//		in markup, this is specified as "disabled='disabled'", or just "disabled"
+	disabled: false,
+
+	// isContainer: Boolean
+	//		can this widget contain other widgets?
+	isContainer: false, 
+
+	// widgetId: String
+	//		a unique, opaque ID string that can be assigned by users or by the
+	//		system. If the developer passes an ID which is known not to be
+	//		unique, the specified ID is ignored and the system-generated ID is
+	//		used instead.
+	widgetId: "",
+
+	// widgetType: String
+	//		used for building generic widgets
+	widgetType: "Widget",
+
+	// ns: String
+	//		defaults to 'dojo'.  "namespace" is a reserved word in JavaScript, so we abbreviate
+	ns: "dojo",
+
+	getNamespacedType: function(){ 
+		// summary:
+		//		get the "full" name of the widget. If the widget comes from the
+		//		"dojo" namespace and is a Button, calling this method will
+		//		return "dojo:button", all lower-case
+		return (this.ns ? this.ns + ":" + this.widgetType : this.widgetType).toLowerCase(); // String
+	},
+	
+	toString: function(){
+		// summary:
+		//		returns a string that represents the widget. When a widget is
+		//		cast to a string, this method will be used to generate the
+		//		output. Currently, it does not implement any sort of reversable
+		//		serialization.
+		return '[Widget ' + this.getNamespacedType() + ', ' + (this.widgetId || 'NO ID') + ']'; // String
+	},
+
+	repr: function(){
+		// summary: returns the string representation of the widget.
+		return this.toString(); // String
+	},
+
+	enable: function(){
+		// summary:
+		//		enables the widget, usually involving unmasking inputs and
+		//		turning on event handlers. Not implemented here.
+		this.disabled = false;
+	},
+
+	disable: function(){
+		// summary:
+		//		disables the widget, usually involves masking inputs and
+		//		unsetting event handlers. Not implemented here.
+		this.disabled = true;
+	},
+
+	// TODO:
+	//	1) this would be better in HtmlWidget rather than here?
+	//	2) since many widgets don't care if they've been resized, maybe this should be a mixin?
+	onResized: function(){
+		// summary:
+		//		A signal that widgets will call when they have been resized.
+		//		Can be connected to for determining if a layout needs to be
+		//		reflowed. Clients should override this function to do special
+		//		processing, then call this.notifyChildrenOfResize() to notify
+		//		children of resize.
+		this.notifyChildrenOfResize();
+	},
+	
+	notifyChildrenOfResize: function(){
+		// summary: dispatches resized events to all children of this widget
+		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, parent, ns){
+		// summary:
+		//		'create' manages the initialization part of the widget
+		//		lifecycle. It's called implicitly when any widget is created.
+		//		All other initialization functions for widgets, except for the
+		//		constructor, are called as a result of 'create' being fired.
+		// args: Object
+		//		a normalized view of the parameters that the widget should take
+		// fragment: Object
+		//		if the widget is being instantiated from markup, this object 
+		// parent: Widget?
+		//		the widget, if any, that this widget will be the child of.  If
+		//		none is passed, the global default widget is used.
+		// ns: String?
+		//		what namespace the widget belongs to
+		// description:
+		//		to understand the process by which widgets are instantiated, it
+		//		is critical to understand what other methods 'create' calls and
+		//		which of them you'll want to over-ride. Of course, adventurous
+		//		developers could over-ride 'create' entirely, but this should
+		//		only be done as a last resort.
+		//
+		//		Below is a list of the methods that are called, in the order
+		//		they are fired, along with notes about what they do and if/when
+		//		you should over-ride them in your widget:
+		//			
+		//			mixInProperties:
+		//				takes the args and does lightweight type introspection
+		//				on pre-existing object properties to initialize widget
+		//				values by casting the values that are passed in args
+		//			postMixInProperties:
+		//				a stub function that you can over-ride to modify
+		//				variables that may have been naively assigned by
+		//				mixInProperties
+		//			# widget is added to manager object here
+		//			buildRendering
+		//				subclasses use this method to handle all UI initialization
+		//			initialize:
+		//				a stub function that you can over-ride.
+		//			postInitialize:
+		//				a stub function that you can over-ride.
+		//			postCreate
+		//				a stub function that you can over-ride to modify take
+		//				actions once the widget has been placed in the UI
+		//
+		//		all of these functions are passed the same arguments as are
+		//		passed to 'create'
+
+		//dojo.profile.start(this.widgetType + " create");
+		if(ns){
+			this.ns = ns;
+		}
+		// dojo.debug(this.widgetType, "create");
+		//dojo.profile.start(this.widgetType + " satisfyPropertySets");
+		this.satisfyPropertySets(args, fragment, parent);
+		//dojo.profile.end(this.widgetType + " satisfyPropertySets");
+		// dojo.debug(this.widgetType, "-> mixInProperties");
+		//dojo.profile.start(this.widgetType + " mixInProperties");
+		this.mixInProperties(args, fragment, parent);
+		//dojo.profile.end(this.widgetType + " mixInProperties");
+		// dojo.debug(this.widgetType, "-> postMixInProperties");
+		//dojo.profile.start(this.widgetType + " postMixInProperties");
+		this.postMixInProperties(args, fragment, parent);
+		//dojo.profile.end(this.widgetType + " postMixInProperties");
+		// dojo.debug(this.widgetType, "-> dojo.widget.manager.add");
+		dojo.widget.manager.add(this);
+		// dojo.debug(this.widgetType, "-> buildRendering");
+		//dojo.profile.start(this.widgetType + " buildRendering");
+		this.buildRendering(args, fragment, parent);
+		//dojo.profile.end(this.widgetType + " buildRendering");
+		// dojo.debug(this.widgetType, "-> initialize");
+		//dojo.profile.start(this.widgetType + " initialize");
+		this.initialize(args, fragment, parent);
+		//dojo.profile.end(this.widgetType + " initialize");
+		// dojo.debug(this.widgetType, "-> postInitialize");
+		// postinitialize includes subcomponent creation
+		// profile is put directly to function
+		this.postInitialize(args, fragment, parent);
+		// dojo.debug(this.widgetType, "-> postCreate");
+		//dojo.profile.start(this.widgetType + " postCreate");
+		this.postCreate(args, fragment, parent);
+		//dojo.profile.end(this.widgetType + " postCreate");
+		// dojo.debug(this.widgetType, "done!");
+		
+		//dojo.profile.end(this.widgetType + " create");
+		
+		return this;
+	},
+
+	destroy: function(finalize){
+		// summary:
+		// 		Destroy this widget and it's descendants. This is the generic
+		// 		"destructor" function that all widget users should call to
+		// 		clealy discard with a widget. Once a widget is destroyed, it's
+		// 		removed from the manager object.
+		// finalize: Boolean
+		//		is this function being called part of global environment
+		//		tear-down?
+
+		// FIXME: this is woefully incomplete
+		if(this.parent){
+			this.parent.removeChild(this);
+		}
+		this.destroyChildren();
+		this.uninitialize();
+		this.destroyRendering(finalize);
+		dojo.widget.manager.removeById(this.widgetId);
+	},
+
+	destroyChildren: function(){
+		// summary:
+		//		Recursively destroy the children of this widget and their
+		//		descendents.
+		var widget;
+		var i=0;
+		while(this.children.length > i){
+			widget = this.children[i];
+			if (widget instanceof dojo.widget.Widget) { // find first widget
+				this.removeChild(widget);
+				widget.destroy();
+				continue;
+			}
+			
+			i++; // skip data object
+		}
+				
+	},
+
+	getChildrenOfType: function(/*String*/type, recurse){
+		// summary: 
+		//		return an array of descendant widgets who match the passed type
+		// recurse: Boolean
+		//		should we try to get all descendants that match? Defaults to
+		//		false.
+		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; // Array
+	},
+
+	getDescendants: function(){
+		// returns: a flattened array of all direct descendants including self
+		var result = [];
+		var stack = [this];
+		var elem;
+		while ((elem = stack.pop())){
+			result.push(elem);
+			// a child may be data object without children field set (not widget)
+			if (elem.children) {
+				dojo.lang.forEach(elem.children, function(elem) { stack.push(elem); });
+			}
+		}
+		return result; // Array
+	},
+
+
+	isFirstChild: function(){
+		return this === this.parent.children[0]; // Boolean
+	},
+
+	isLastChild: function() {
+		return this === this.parent.children[this.parent.children.length-1]; // Boolean
+	},
+
+	satisfyPropertySets: function(args){
+		// summary: not implemented!
+
+		// 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, /*Object*/frag){
+		// summary:
+		// 		takes the list of properties listed in args and sets values of
+		// 		the current object based on existence of properties with the
+		// 		same name (case insensitive) and the type of the pre-existing
+		// 		property. This is a lightweight conversion and is not intended
+		// 		to capture custom type semantics.
+		// args: Object
+		//		A map of properties and values to set on the current object. By
+		//		default it is assumed that properties in args are in string
+		//		form and need to be converted. However, if there is a
+		//		'fastMixIn' property with the value 'true' in the args param,
+		//		this assumption is ignored and all values in args are copied
+		//		directly to the current object without any form of type
+		//		casting.
+		// description:
+		//		The mix-in code attempts to do some type-assignment based on
+		//		PRE-EXISTING properties of the "this" object. When a named
+		//		property of args 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, it is first cast using "new
+		//		Function()" and the execution scope modified such that it
+		//		always evaluates in the context of the current object. This
+		//		listener is then added to the original function via
+		//		dojo.event.connect(). 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. Properties
+		//		which do not occur in the "this" object are assigned to the
+		//		this.extraArgs map using both the original name and the
+		//		lower-case name of the property. This allows for consistent
+		//		access semantics regardless of the case preservation of the
+		//		source of the property names.
+		
+		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");
+
+		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.kwConnect({
+								srcObj: this, 
+								srcFunc: x, 
+								adviceObj: this, 
+								adviceFunc: 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 a plain string is passed to a property of type dojo.uri.Uri,
+						// we assume it is relative to root of dojo
+						if (this[x] instanceof dojo.uri.Uri){
+							this[x] = dojo.uri.dojoUri(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(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+		// summary
+		//	Called after the parameters to the widget have been read-in,
+		//	but before the widget template is instantiated.
+		//	Especially useful to set properties that are referenced in the widget template.
+	},
+
+	initialize: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+		// summary: stub function.
+		return false;
+		// dojo.unimplemented("dojo.widget.Widget.initialize");
+	},
+
+	postInitialize: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+		// summary: stub function.
+		return false;
+	},
+
+	postCreate: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+		// summary: stub function.
+		return false;
+	},
+
+	uninitialize: function(){
+		// summary: 
+		//		stub function. Over-ride to implement custom widget tear-down
+		//		behavior.
+		return false;
+	},
+
+	buildRendering: function(/*Object*/args, /*Object*/frag, /*Widget*/parent){
+		// summary: stub function. SUBCLASSES MUST IMPLEMENT
+		dojo.unimplemented("dojo.widget.Widget.buildRendering, on "+this.toString()+", ");
+		return false;
+	},
+
+	destroyRendering: function(){
+		// summary: stub function. SUBCLASSES MUST IMPLEMENT
+		dojo.unimplemented("dojo.widget.Widget.destroyRendering");
+		return false;
+	},
+
+	addedTo: function(parent){
+		// summary:
+		//		stub function this is just a signal that can be caught
+		// parent: Widget
+		//		instance of dojo.widget.Widget that we were added to
+	},
+
+	addChild: function(child){
+		// summary: stub function. 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*/widget){
+		// summary: 
+		//		removes the passed widget instance from this widget but does
+		//		not destroy it
+		for(var x=0; x<this.children.length; x++){
+			if(this.children[x] === widget){
+				this.children.splice(x, 1);
+				widget.parent=null;
+				break;
+			}
+		}
+		return widget; // Widget
+	},
+
+	getPreviousSibling: function(){
+		// summary:
+		//		returns null if this is the first child of the parent,
+		//		otherwise returns the next sibling to the "left".
+		var idx = this.getParentIndex();
+ 
+		 // first node is idx=0 not found is idx<0
+		if (idx<=0) return null;
+ 
+		return this.parent.children[idx-1]; // Widget
+	},
+ 
+	getSiblings: function(){
+		// summary: gets an array of all children of our parent, including "this"
+		return this.parent.children; // Array
+	},
+ 
+	getParentIndex: function(){
+		// summary: what index are we at in the parent's children array?
+		return dojo.lang.indexOf(this.parent.children, this, true); // int
+	},
+ 
+	getNextSibling: function(){
+		// summary:
+		//		returns null if this is the last child of the parent,
+		//		otherwise returns the next sibling to the "right".
+ 
+		var idx = this.getParentIndex();
+ 
+		if (idx == this.parent.children.length-1){return null;} // last node
+		if (idx < 0){return null;} // not found
+ 
+		return this.parent.children[idx+1]; // Widget
+	}
+});
+
+// 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(/*String*/type){
+	// summary: deprecated!
+	dojo.deprecated("addParseTreeHandler", ". ParseTreeHandlers are now reserved for components. Any unfiltered DojoML tag without a ParseTreeHandler is assumed to be a widget", "0.5");
+	/*
+	var ltype = type.toLowerCase();
+	this[ltype] = function(fragment, widgetParser, parentComp, insertionIndex, localProps){
+		var _ltype = ltype;
+		dojo.profile.start(_ltype);
+		var n = dojo.widget.buildWidgetFromParseTree(ltype, fragment, widgetParser, parentComp, insertionIndex, localProps);
+		dojo.profile.end(_ltype);
+		return n;
+	}
+	*/
+}
+
+//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(/*String*/				type,
+												/*Object*/				frag, 
+												/*dojo.widget.Parse*/	parser,
+												/*Widget, optional*/	parentComp, 
+												/*int, optional*/		insertionIndex,
+												/*Object*/				localProps){
+
+	// summary: creates a tree of widgets from the data structure produced by the first-pass parser (frag)
+	
+	// test for accessibility mode 
+	dojo.a11y.setAccessibleMode();
+	//dojo.profile.start("buildWidgetFromParseTree");
+	// FIXME: for codepath from createComponentFromScript, we are now splitting a path 
+	// that we already split and then joined
+	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[frag["ns"]+":"+stype]);
+	var twidget = dojo.widget.manager.getImplementation(stype,null,null,frag["ns"]);
+	if(!twidget){
+		throw new Error('cannot find "' + type + '" widget');
+	}else if (!twidget.create){
+		throw new Error('"' + type + '" widget object has no "create" method and does not appear to implement *Widget');
+	}
+	localProperties["dojoinsertionindex"] = insertionIndex;
+	// FIXME: we lose no less than 5ms in construction!
+	var ret = twidget.create(localProperties, frag, parentComp, frag["ns"]);
+	// dojo.profile.end("buildWidgetFromParseTree");
+	return ret;
+}
+
+dojo.widget.defineWidget = function(widgetClass, renderer, superclasses, init, props){
+	// summary: Create a widget constructor function (aka widgetClass)
+	// widgetClass: String
+	//		the location in the object hierarchy to place the new widget class constructor
+	// renderer: String
+	//		usually "html", determines when this delcaration will be used
+	// superclasses: Function||Function[]
+	//		can be either a single function or an array of functions to be
+	//		mixed in as superclasses. If an array, only the first will be used
+	//		to set prototype inheritance.
+	// init: Function
+	//		an optional constructor function. Will be called after superclasses are mixed in.
+	// props: Object
+	//		a map of properties and functions to extend the class prototype with
+
+	// This meta-function does parameter juggling for backward compat and overloading
+	// if 4th argument is a string, we are using the old syntax
+	// old sig: widgetClass, superclasses, props (object), renderer (string), init (function)
+	if(dojo.lang.isString(arguments[3])){
+		dojo.widget._defineWidget(arguments[0], arguments[3], arguments[1], arguments[4], arguments[2]);
+	}else{
+		// widgetClass
+		var args = [ arguments[0] ], p = 3;
+		if(dojo.lang.isString(arguments[1])){
+			// renderer, superclass
+			args.push(arguments[1], arguments[2]);
+		}else{
+			// superclass
+			args.push('', arguments[1]);
+			p = 2;
+		}
+		if(dojo.lang.isFunction(arguments[p])){
+			// init (function), props (object) 
+			args.push(arguments[p], arguments[p+1]);
+		}else{
+			// props (object) 
+			args.push(null, arguments[p]);
+		}
+		dojo.widget._defineWidget.apply(this, args);
+	}
+}
+
+dojo.widget.defineWidget.renderers = "html|svg|vml";
+
+dojo.widget._defineWidget = function(widgetClass /*string*/, renderer /*string*/, superclasses /*function||array*/, init /*function*/, props /*object*/){
+	// FIXME: uncomment next line to test parameter juggling ... remove when confidence improves
+	// dojo.debug('(c:)' + widgetClass + '\n\n(r:)' + renderer + '\n\n(i:)' + init + '\n\n(p:)' + props);
+	// widgetClass takes the form foo.bar.baz<.renderer>.WidgetName (e.g. foo.bar.baz.WidgetName or foo.bar.baz.html.WidgetName)
+	var module = widgetClass.split(".");
+	var type = module.pop(); // type <= WidgetName, module <= foo.bar.baz<.renderer>
+	var regx = "\\.(" + (renderer ? renderer + '|' : '') + dojo.widget.defineWidget.renderers + ")\\.";
+	var r = widgetClass.search(new RegExp(regx));
+	module = (r < 0 ? module.join(".") : widgetClass.substr(0, r));
+
+	// deprecated in favor of namespace system, remove for 0.5
+	dojo.widget.manager.registerWidgetPackage(module);
+	
+	var pos = module.indexOf(".");
+	var nsName = (pos > -1) ? module.substring(0,pos) : module;
+
+	// FIXME: hrm, this might make things simpler
+	//dojo.widget.tags.addParseTreeHandler(nsName+":"+type.toLowerCase());
+	
+	props=(props)||{};
+	props.widgetType = type;
+	if((!init)&&(props["classConstructor"])){
+		init = props.classConstructor;
+		delete props.classConstructor;
+	}
+	dojo.declare(widgetClass, superclasses, init, props);
+}