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 [24/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/html/MenuItem.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MenuItem.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MenuItem.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MenuItem.js Mon May 22 16:10:12 2006
@@ -0,0 +1,60 @@
+/*
+	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.MenuItem");
+
+/* HtmlMenuItem
+ ***************/
+
+dojo.widget.html.MenuItem = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.MenuItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.MenuItem, {
+	widgetType: "MenuItem",
+	templateString: '<li class="dojoMenuItem" dojoAttachEvent="onMouseOver; onMouseOut; onMouseDown; onMouseUp; onClick;"></li>',
+	title: "",
+
+	fillInTemplate: function(args, frag){
+		dojo.html.disableSelection(this.domNode);
+
+		if(!dojo.string.isBlank(this.title)){
+			this.domNode.appendChild(document.createTextNode(this.title));
+		}else{
+			this.domNode.appendChild(frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"]);
+		}
+	},
+	
+	onMouseOver: function(e){
+		dojo.html.addClass(this.domNode, "dojoMenuItemHover");
+	},
+	
+	onMouseOut: function(e){
+		dojo.html.removeClass(this.domNode, "dojoMenuItemHover");
+	},
+	
+	onClick: function(e){ this.onSelect(this, e); },
+	onMouseDown: function(e){},
+	onMouseUp: function(e){},
+	
+	// By default, when I am clicked, click the item inside of me
+	onSelect: function (item, e) {
+		var child = dojo.dom.getFirstChildElement(this.domNode);
+		if(child){
+			if(child.click){
+				child.click();
+			}else if(child.href){
+				location.href = child.href;
+			}
+		}
+	}
+});
+

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MonthlyCalendar.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MonthlyCalendar.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MonthlyCalendar.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/MonthlyCalendar.js Mon May 22 16:10:12 2006
@@ -0,0 +1,134 @@
+/*
+	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.MonthlyCalendar");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.DatePicker");
+dojo.require("dojo.widget.MonthlyCalendar");
+//dojo.require("dojo.widget.MonthlyCalendar.util");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+
+dojo.widget.html.MonthlyCalendar= function(){
+	dojo.widget.MonthlyCalendar.call(this);
+	//dojo.widget.html.DatePicker.call(this);
+	this.widgetType = "MonthlyCalendar";
+	this.templatePath =  dojo.uri.dojoUri("src/widget/templates/HtmlMonthlyCalendar.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlMonthlyCalendar.css");
+
+	this.iCalendars = [];
+}
+
+dojo.inherits(dojo.widget.html.MonthlyCalendar, dojo.widget.html.DatePicker);
+
+dojo.lang.extend(dojo.widget.html.MonthlyCalendar, {
+	cache: function() {
+	},
+
+	addCalendar: function(/* dojo.iCalendar */ cal) {
+		dojo.debug("Adding Calendar");
+		this.iCalendars.push(cal);
+		dojo.debug("Starting init");
+		this.initUI()
+		dojo.debug("done init");
+	},
+
+	createDayContents: function(node,mydate) {
+		dojo.dom.removeChildren(node);
+		node.appendChild(document.createTextNode(mydate.getDate()));	
+			for(var x=0; x<this.iCalendars.length; x++) {
+				var evts = this.iCalendars[x].getEvents(mydate);
+				if ((dojo.lang.isArray(evts)) && (evts.length>0)) {
+				for(var y=0;y<evts.length;y++) {
+					var el = document.createElement("div");
+					dojo.html.addClass(el, "dojoMonthlyCalendarEvent");          
+					el.appendChild(document.createTextNode(evts[y].summary.value));
+					el.width = dojo.style.getContentWidth(node);
+					node.appendChild(el);
+				}
+			}
+		}
+	},
+
+	initUI: function() {
+		this.selectedIsUsed = false;
+		this.currentIsUsed = false;
+		var currentClassName = "";
+		var previousDate = new Date();
+		var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td");
+		var currentCalendarNode;
+		// set hours of date such that there is no chance of rounding error due to 
+		// time change in local time zones
+		previousDate.setHours(8);
+		var nextDate = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date, 8);
+		var lastDay = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date + 42, 8);
+		
+		if (this.iCalendars.length > 0) {
+			for (var x=0; x<this.iCalendars.length;x++) {
+				this.iCalendars[x].preComputeRecurringEvents(lastDay);
+			}
+		}
+
+		if(this.firstSaturday.date < 7) {
+			// this means there are days to show from the previous month
+			var dayInWeek = 6;
+			for (var i=this.firstSaturday.date; i>0; i--) {
+				currentCalendarNode = calendarNodes.item(dayInWeek);
+				this.createDayContents(currentCalendarNode, nextDate);
+				
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+				dayInWeek--;
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, false);
+			}
+			for(var i=dayInWeek; i>-1; i--) {
+				currentCalendarNode = calendarNodes.item(i);
+
+				this.createDayContents(currentCalendarNode, nextDate);
+
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "previous"));
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, false);				
+			}
+		} else {
+			nextDate.setDate(1);
+			for(var i=0; i<7; i++) {
+				currentCalendarNode = calendarNodes.item(i);
+				this.createDayContents(currentCalendarNode, nextDate);
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, true);				
+			}
+		}
+		previousDate.setDate(this.firstSaturday.date);
+		previousDate.setMonth(this.firstSaturday.month);
+		previousDate.setFullYear(this.firstSaturday.year);
+		nextDate = this.incrementDate(previousDate, true);
+		var count = 7;
+		currentCalendarNode = calendarNodes.item(count);
+		while((nextDate.getMonth() == previousDate.getMonth()) && (count<42)) {
+			this.createDayContents(currentCalendarNode, nextDate);
+			dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+			currentCalendarNode = calendarNodes.item(++count);
+			previousDate = nextDate;
+			nextDate = this.incrementDate(nextDate, true);
+		}
+		
+		while(count < 42) {
+			this.createDayContents(currentCalendarNode, nextDate);
+			dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "next"));
+			currentCalendarNode = calendarNodes.item(++count);
+			previousDate = nextDate;
+			nextDate = this.incrementDate(nextDate, true);
+		}
+		this.setMonthLabel(this.firstSaturday.month);
+		this.setYearLabels(this.firstSaturday.year);
+	}	
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/SortableTable.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/SortableTable.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/SortableTable.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/SortableTable.js Mon May 22 16:10:12 2006
@@ -0,0 +1,582 @@
+/*
+	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.SortableTable");
+dojo.require("dojo.lang");
+dojo.require("dojo.date");
+dojo.require("dojo.html");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.SortableTable");
+
+dojo.widget.html.SortableTable=function(){
+	//	summary
+	//	Constructor for the SortableTable widget
+	dojo.widget.SortableTable.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	this.headClass="";
+	this.tbodyClass="";
+	this.headerClass="";
+	this.headerSortUpClass="selected";
+	this.headerSortDownClass="selected";
+	this.rowClass="";
+	this.rowAlternateClass="alt";
+	this.rowSelectedClass="selected";
+	this.columnSelected="sorted-column";
+};
+dojo.inherits(dojo.widget.html.SortableTable, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.SortableTable, {
+	templatePath:null,
+	templateCssPath:null,
+
+	getTypeFromString:function(/* string */ s){
+		//	summary
+		//	Find the constructor that matches param s by searching through the entire object tree.
+		var parts=s.split("."),i=0,obj=dj_global; 
+		do{obj=obj[parts[i++]];}while(i<parts.length&&obj); 
+		return(obj!=dj_global)?obj:null;	//	function
+	},
+	compare:function(/* object */ o1, /* object */ o2){
+		//	summary
+		//	Compare two objects using a shallow property compare
+		for(var p in o1){
+			if(!(p in o2)) return false;	//	boolean
+			if(o1[p].valueOf()!=o2[p].valueOf()) return false;	//	boolean
+		}
+		return true;	// boolean
+	},
+	isSelected:function(/* object */ o){
+		//	summary
+		//	checked to see if the passed object is in the current selection.
+		for(var i=0;i<this.selected.length;i++){
+			if(this.compare(this.selected[i],o)){
+				return true; // boolean
+			}
+		}
+		return false;	// boolean
+	},
+	removeFromSelected:function(/* object */ o){
+		//	summary
+		//	remove the passed object from the current selection.
+		var idx=-1;
+		for(var i=0;i<this.selected.length;i++){
+			if(this.compare(this.selected[i],o)){
+				idx=i;
+				break;
+			}
+		}
+		if(idx>=0){
+			this.selected.splice(idx,1);
+		}
+	},
+	getSelection:function(){
+		//	summary
+		//	return the array of currently selected objects (JSON format)
+		return this.selected;	//	array
+	},
+	getValue:function(){
+		//	summary
+		//	return a comma-delimited list of selected valueFields.
+		var a=[];
+		for(var i=0;i<this.selected.length;i++){
+			if (this.selected[i][this.valueField]){
+				a.push(this.selected[i][this.valueField]);
+			}
+		}
+		return a.join();	//	string
+	},
+	reset:function(){
+		//	summary
+		//	completely resets the internal representations.
+		this.columns=[];
+		this.data=[];
+		this.resetSelections(this.domNode.getElementsByTagName("tbody")[0]);
+	},
+	resetSelections:function(/* HTMLTableBodyElement */ body){
+		this.selected=[];
+		var idx=0;
+		var rows=body.getElementsByTagName("tr");
+		for(var i=0; i<rows.length; i++){
+			if(rows[i].parentNode==body){
+				rows[i].removeAttribute("selected");
+				if(this.enableAlternateRows&&idx%2==1){
+					rows[i].className=this.rowAlternateClass;
+				}else{
+					rows[i].className="";
+				}
+				idx++;
+			}
+		}
+	},
+
+	getObjectFromRow:function(/* HTMLTableRowElement */ row){
+		//	summary
+		//	creates a JSON object based on the passed row
+		var cells=row.getElementsByTagName("td");
+		var o={};
+		for(var i=0; i<this.columns.length;i++){
+			if(this.columns[i].sortType=="__markup__"){
+				//	FIXME: should we parse this instead?  Because if the user may not get back the markup they put in...
+				o[this.columns[i].getField()]=cells[i].innerHTML;
+			}else{
+				var text=dojo.html.renderedTextContent(cells[i]);
+				var val=new (this.columns[i].getType())(text);
+				o[this.columns[i].getField()]=val;
+			}
+		}
+		if(dojo.html.hasAttribute(row,"value")){
+			o[this.valueField]=dojo.html.getAttribute(row,"value");
+		}
+		return o;	//	object
+	},
+	setSelectionByRow:function(/* HTMLTableElementRow */ row){
+		//	summary
+		//	create the selection object based on the passed row, makes sure it's unique.
+		//	note that you need to call render manually (because of multi-select operations)
+		var o=this.getObjectFromRow(row);
+		var b=false;
+		for(var i=0;i<this.selected.length;i++){
+			if(this.compare(this.selected[i], o)){
+				b=true;
+				break;
+			}
+		}
+		if(!b){
+			this.selected.push(o);
+		}
+	},
+
+	parseColumns:function(/* HTMLTableHeadElement */ node){
+		//	summary
+		//	parses the passed element to create column objects
+		this.reset();
+		var row=node.getElementsByTagName("tr")[0];
+		var cells=row.getElementsByTagName("td");
+		if (cells.length==0) cells=row.getElementsByTagName("th");
+		for(var i=0; i<cells.length; i++){
+			var o={
+				field:null,
+				format:null,
+				noSort:false,
+				sortType:"String",
+				dataType:String,
+				sortFunction:null,
+				label:null,
+				align:"left",
+				valign:"middle",
+				getField:function(){ return this.field||this.label; },
+				getType:function(){ return this.dataType; }
+			};
+			//	presentation attributes
+			if(dojo.html.hasAttribute(cells[i], "align")){
+				o.align=dojo.html.getAttribute(cells[i],"align");
+			}
+			if(dojo.html.hasAttribute(cells[i], "valign")){
+				o.valign=dojo.html.getAttribute(cells[i],"valign");
+			}
+
+			//	sorting features.
+			if(dojo.html.hasAttribute(cells[i], "nosort")){
+				o.noSort=dojo.html.getAttribute(cells[i],"nosort")=="true";
+			}
+			if(dojo.html.hasAttribute(cells[i], "sortusing")){
+				var trans=dojo.html.getAttribute(cells[i],"sortusing");
+				var f=this.getTypeFromString(trans);
+				if (f!=null && f!=window && typeof(f)=="function") 
+					o.sortFunction=f;
+			}
+
+			if(dojo.html.hasAttribute(cells[i], "field")){
+				o.field=dojo.html.getAttribute(cells[i],"field");
+			}
+			if(dojo.html.hasAttribute(cells[i], "format")){
+				o.format=dojo.html.getAttribute(cells[i],"format");
+			}
+			if(dojo.html.hasAttribute(cells[i], "dataType")){
+				var sortType=dojo.html.getAttribute(cells[i],"dataType");
+				if(sortType.toLowerCase()=="html"||sortType.toLowerCase()=="markup"){
+					o.sortType="__markup__";	//	always convert to "__markup__"
+					o.noSort=true;
+				}else{
+					var type=this.getTypeFromString(sortType);
+					if(type){
+						o.sortType=sortType;
+						o.dataType=type;
+					}
+				}
+			}
+			o.label=dojo.html.renderedTextContent(cells[i]);
+			this.columns.push(o);
+
+			//	check to see if there's a default sort, and set the properties necessary
+			if(dojo.html.hasAttribute(cells[i], "sort")){
+				this.sortIndex=i;
+				var dir=dojo.html.getAttribute(cells[i], "sort");
+				if(!isNaN(parseInt(dir))){
+					dir=parseInt(dir);
+					this.sortDirection=(dir!=0)?1:0;
+				}else{
+					this.sortDirection=(dir.toLowerCase()=="desc")?1:0;
+				}
+			}
+		}
+	},
+
+	parseData:function(/* array */ data){
+		//	summary
+		//	Parse the passed JSON data structure, and cast based on columns.
+		this.data=[];
+		this.selected=[];
+		for(var i=0; i<data.length; i++){
+			var o={};	//	new data object.
+			for(var j=0; j<this.columns.length; j++){
+				var field=this.columns[j].getField();
+				if(this.columns[j].sortType=="__markup__"){
+					o[field]=String(data[i][field]);
+				}else{
+					var type=this.columns[j].getType();
+					var val=data[i][field];
+					var t=this.columns[j].sortType.toLowerCase();
+					if(val){
+						o[field]=new type(val);
+					}else{
+						o[field]=new type();	//	let it use the default.
+					}
+				}
+			}
+			//	check for the valueField if not already parsed.
+			if(data[i][this.valueField]&&!o[this.valueField]){
+				o[this.valueField]=data[i][this.valueField];
+			}
+			this.data.push(o);
+		}
+	}, 
+
+	parseDataFromTable:function(/* HTMLTableBodyElement */ tbody){
+		//	summary
+		//	parses the data in the tbody of a table to create a set of objects.
+		//	Will add objects to this.selected if an attribute 'selected="true"' is present on the row.
+		this.data=[];
+		this.selected=[];
+		var rows=tbody.getElementsByTagName("tr");
+		for(var i=0; i<rows.length; i++){
+			if(dojo.html.getAttribute(rows[i],"ignoreIfParsed")=="true"){
+				continue;
+			}
+			var o={};	//	new data object.
+			var cells=rows[i].getElementsByTagName("td");
+			for(var j=0; j<this.columns.length; j++){
+				var field=this.columns[j].getField();
+				if(this.columns[j].sortType=="__markup__"){
+					//	FIXME: parse this?
+					o[field]=cells[j].innerHTML;
+				}else{
+					var type=this.columns[j].getType();
+					var val=dojo.html.renderedTextContent(cells[j]); //	should be the same index as the column.
+					if (val) o[field]=new type(val);
+					else o[field]=new type();	//	let it use the default.
+				}
+			}
+			if(dojo.html.hasAttribute(rows[i],"value")&&!o[this.valueField]){
+				o[this.valueField]=dojo.html.getAttribute(rows[i],"value");
+			}
+			//	FIXME: add code to preserve row attributes in __metadata__ field?
+			this.data.push(o);
+			
+			//	add it to the selections if selected="true" is present.
+			if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+				this.selected.push(o);
+			}
+		}
+	},
+	
+	showSelections:function(){
+		var body=this.domNode.getElementsByTagName("tbody")[0];
+		var rows=body.getElementsByTagName("tr");
+		var idx=0;
+		for(var i=0; i<rows.length; i++){
+			if(rows[i].parentNode==body){
+				if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+					rows[i].className=this.rowSelectedClass;
+				} else {
+					if(this.enableAlternateRows&&idx%2==1){
+						rows[i].className=this.rowAlternateClass;
+					}else{
+						rows[i].className="";
+					}
+				}
+				idx++;
+			}
+		}
+	},
+	render:function(bDontPreserve){
+		//	summary
+		//	renders the table to the browser
+		var data=[];
+		var body=this.domNode.getElementsByTagName("tbody")[0];
+
+		if(!bDontPreserve){
+			//	rebuild data and selection
+			this.parseDataFromTable(body);
+		}
+
+		//	clone this.data for sorting purposes.
+		for(var i=0; i<this.data.length; i++){
+			data.push(this.data[i]);
+		}
+		
+		var col=this.columns[this.sortIndex];
+		if(!col.noSort){
+			var field=col.getField();
+			if(col.sortFunction){
+				var sort=col.sortFunction;
+			}else{
+				var sort=function(a,b){
+					if (a[field]>b[field]) return 1;
+					if (a[field]<b[field]) return -1;
+					return 0;
+				}
+			}
+			data.sort(sort);
+			if(this.sortDirection!=0) data.reverse();
+		}
+
+		//	build the table and pop it in.
+		while(body.childNodes.length>0) body.removeChild(body.childNodes[0]);
+		for(var i=0; i<data.length;i++){
+			var row=document.createElement("tr");
+			dojo.html.disableSelection(row);
+			if (data[i][this.valueField]){
+				row.setAttribute("value",data[i][this.valueField]);
+			}
+			if(this.isSelected(data[i])){
+				row.className=this.rowSelectedClass;
+				row.setAttribute("selected","true");
+			} else {
+				if(this.enableAlternateRows&&i%2==1){
+					row.className=this.rowAlternateClass;
+				}
+			}
+			for(var j=0;j<this.columns.length;j++){
+				var cell=document.createElement("td");
+				cell.setAttribute("align", this.columns[j].align);
+				cell.setAttribute("valign", this.columns[j].valign);
+				dojo.html.disableSelection(cell);
+				if(this.sortIndex==j){
+					cell.className=this.columnSelected;
+				}
+				if(this.columns[j].sortType=="__markup__"){
+					cell.innerHTML=data[i][this.columns[j].getField()];
+					for(var k=0; k<cell.childNodes.length; k++){
+						var node=cell.childNodes[k];
+						if(node&&node.nodeType==dojo.html.ELEMENT_NODE){
+							dojo.html.disableSelection(node);
+						}
+					}
+				}else{
+					if(this.columns[j].getType()==Date){
+						var format=this.defaultDateFormat;
+						if(this.columns[j].format) format=this.columns[j].format;
+						cell.appendChild(document.createTextNode(dojo.date.format(data[i][this.columns[j].getField()], format)));
+					}else{
+						cell.appendChild(document.createTextNode(data[i][this.columns[j].getField()]));
+					}
+				}
+				row.appendChild(cell);
+			}
+			body.appendChild(row);
+			dojo.event.connect(row, "onclick", this, "onUISelect");
+		}
+		
+		//	if minRows exist.
+		var minRows=parseInt(this.minRows);
+		if (!isNaN(minRows) && minRows>0 && data.length<minRows){
+			var mod=0;
+			if(data.length%2==0) mod=1;
+			var nRows=minRows-data.length;
+			for(var i=0; i<nRows; i++){
+				var row=document.createElement("tr");
+				row.setAttribute("ignoreIfParsed","true");
+				if(this.enableAlternateRows&&i%2==mod){
+					row.className=this.rowAlternateClass;
+				}
+				for(var j=0;j<this.columns.length;j++){
+					var cell=document.createElement("td");
+					cell.appendChild(document.createTextNode("\u00A0"));
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+	},
+
+	//	the following the user can override.
+	onSelect:function(/* DomEvent */ e){ 
+		//	summary
+		//	empty function for the user to attach code to, fired by onUISelect
+	},
+	onUISelect:function(/* DomEvent */ e){
+		//	summary
+		//	fired when a user selects a row
+		var row=dojo.html.getParentByType(e.target,"tr");
+		var body=dojo.html.getParentByType(row,"tbody");
+		if(this.enableMultipleSelect){
+			if(e.metaKey||e.ctrlKey){
+				if(this.isSelected(this.getObjectFromRow(row))){
+					this.removeFromSelected(this.getObjectFromRow(row));
+					row.removeAttribute("selected");
+				}else{
+					//	push onto the selection stack.
+					this.setSelectionByRow(row);
+					row.setAttribute("selected","true");
+				}
+			}else if(e.shiftKey){
+				//	the tricky one.  We need to figure out the *last* selected row above, 
+				//	and select all the rows in between.
+				var startRow;
+				var rows=body.getElementsByTagName("tr");
+				//	if there's a selection above, we go with that first. 
+				for(var i=0;i<rows.length;i++){
+					if(rows[i].parentNode==body){
+						if(rows[i]==row) break;
+						if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+							startRow=rows[i];
+						}
+					}
+				}
+				//	if there isn't a selection above, we continue with a selection below.
+				if(!startRow){
+					startRow=row;
+					for(;i<rows.length;i++){
+						if(dojo.html.getAttribute(rows[i],"selected")=="true"){
+							row=rows[i];
+							break;
+						}
+					}
+				}
+				this.resetSelections(body);
+				if(startRow==row){
+					//	this is the only selection
+					row.setAttribute("selected","true");
+					this.setSelectionByRow(row);
+				}else{
+					var doSelect=false;
+					for(var i=0; i<rows.length; i++){
+						if(rows[i].parentNode==body){
+							rows[i].removeAttribute("selected");
+							if(rows[i]==startRow){
+								doSelect=true;
+							}
+							if(doSelect){
+								this.setSelectionByRow(rows[i]);
+								rows[i].setAttribute("selected","true");
+							}
+							if(rows[i]==row){
+								doSelect=false;
+							}
+						}
+					}
+				}
+			}else{
+				//	reset the selection
+				this.resetSelections(body);
+				row.setAttribute("selected","true");
+				this.setSelectionByRow(row);
+			}
+		}else{
+			//	reset the data selection and go.
+			this.resetSelections(body);
+			row.setAttribute("selected","true");
+			this.setSelectionByRow(row);
+		}
+		this.showSelections();
+		this.onSelect(e);
+		e.stopPropagation();
+		e.preventDefault();
+	},
+	onHeaderClick:function(/* DomEvent */ e){
+		//	summary
+		//	Main handler function for each header column click.
+		var oldIndex=this.sortIndex;
+		var oldDirection=this.sortDirection;
+		var source=e.target;
+		var row=dojo.html.getParentByType(source,"tr");
+		var cellTag="td";
+		if(row.getElementsByTagName(cellTag).length==0) cellTag="th";
+
+		var headers=row.getElementsByTagName(cellTag);
+		var header=dojo.html.getParentByType(source,cellTag);
+		
+		for(var i=0; i<headers.length; i++){
+			if(headers[i]==header){
+				if(i!=oldIndex){
+					//	new col.
+					this.sortIndex=i;
+					this.sortDirection=0;
+					headers[i].className=this.headerSortDownClass
+				}else{
+					this.sortDirection=(oldDirection==0)?1:0;
+					if(this.sortDirection==0){
+						headers[i].className=this.headerSortDownClass;
+					}else{
+						headers[i].className=this.headerSortUpClass;
+					}
+				}
+			}else{
+				//	reset the header class.
+				headers[i].className=this.headerClass;
+			}
+		}
+		this.render();
+	},
+
+	postCreate:function(){ 
+		// 	summary
+		//	overridden from HtmlWidget, initializes and renders the widget.
+		var thead=this.domNode.getElementsByTagName("thead")[0];
+		if(this.headClass.length>0){
+			thead.className=this.headClass;
+		}
+
+		//	disable selections
+		dojo.html.disableSelection(this.domNode);
+
+		//	parse the columns.
+		this.parseColumns(thead);
+
+		//	attach header handlers.
+		var header="td";
+		if(thead.getElementsByTagName(header).length==0) header="th";
+		var headers=thead.getElementsByTagName(header);
+		for(var i=0; i<headers.length; i++){
+			if(!this.columns[i].noSort){
+				dojo.event.connect(headers[i], "onclick", this, "onHeaderClick");
+			}
+			if(this.sortIndex==i){
+				if(this.sortDirection==0){
+					headers[i].className=this.headerSortDownClass;
+				}else{
+					headers[i].className=this.headerSortUpClass;
+				}
+			}
+		}
+
+		//	parse the tbody element and re-render it.
+		var tbody=this.domNode.getElementsByTagName("tbody")[0];
+		if (this.tbodyClass.length>0) {
+			tbody.className=this.tbodyClass;
+		}
+
+		this.parseDataFromTable(tbody);
+		this.render(true);
+	}
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TaskBar.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TaskBar.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TaskBar.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TaskBar.js Mon May 22 16:10:12 2006
@@ -0,0 +1,82 @@
+/*
+	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.TaskBar");
+dojo.provide("dojo.widget.html.TaskBarItem");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.FloatingPane");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event");
+
+// Icon associated w/a floating pane
+dojo.widget.html.TaskBarItem = function(){
+	dojo.widget.TaskBarItem.call(this);
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.TaskBarItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TaskBarItem, {
+	// constructor arguments
+	iconSrc: '',
+	caption: 'Untitled',
+	window: null,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTaskBarItemTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTaskBar.css"),
+
+	fillInTemplate: function() {
+		if ( this.iconSrc != '' ) {
+			var img = document.createElement("img");
+			img.src = this.iconSrc;
+			this.domNode.appendChild(img);
+		}
+		this.domNode.appendChild(document.createTextNode(this.caption));
+		dojo.html.disableSelection(this.domNode);
+	},
+
+	postCreate: function() {
+		this.window=dojo.widget.getWidgetById(this.windowId);
+		this.window.explodeSrc = this.domNode;
+		dojo.event.connect(this.window, "destroy", this, "destroy")
+	},
+
+	onClick: function() {
+		this.window.show();
+	}
+});
+
+// Collection of widgets in a bar, like Windows task bar
+dojo.widget.html.TaskBar = function(){
+
+	dojo.widget.html.FloatingPane.call(this);
+	dojo.widget.TaskBar.call(this);
+	this.titleBarDisplay = "none";
+	this._addChildStack = [];
+}
+
+dojo.inherits(dojo.widget.html.TaskBar, dojo.widget.html.FloatingPane);
+
+dojo.lang.extend(dojo.widget.html.TaskBar, {
+	addChild: function(child) {
+		if(!this.containerNode){ 
+			this._addChildStack.push(child);
+		}else if(this._addChildStack.length > 0){
+			var oarr = this._addChildStack;
+			this._addChildStack = [];
+			dojo.lang.forEach(oarr, function(c){ this.addChild(c); }, this);
+		}
+		var tbi = dojo.widget.createWidget("TaskBarItem",
+			{	windowId: child.widgetId, 
+				caption: child.title, 
+				iconSrc: child.iconSrc
+			});
+		dojo.widget.html.TaskBar.superclass.addChild.call(this,tbi);
+	}
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TimePicker.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TimePicker.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TimePicker.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TimePicker.js Mon May 22 16:10:12 2006
@@ -0,0 +1,249 @@
+/*
+	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.TimePicker");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.TimePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.date");
+dojo.require("dojo.html");
+
+dojo.widget.html.TimePicker = function(){
+	dojo.widget.TimePicker.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+
+	var _this = this;
+	// selected time, JS Date object
+	this.time = "";
+	// set following flag to true if a default time should be set
+	this.useDefaultTime = false;
+	// set the following to true to set default minutes to current time, false to // use zero
+	this.useDefaultMinutes = false;
+	// rfc 3339 date
+	this.storedTime = "";
+	// time currently selected in the UI, stored in hours, minutes, seconds in the format that will be actually displayed
+	this.currentTime = {};
+	this.classNames = {
+		selectedTime: "selectedItem"
+	}
+	this.any = "any"
+	// dom node indecies for selected hour, minute, amPm, and "any time option"
+	this.selectedTime = {
+		hour: "",
+		minute: "",
+		amPm: "",
+		anyTime: false
+	}
+
+	// minutes are ordered as follows: ["12", "6", "1", "7", "2", "8", "3", "9", "4", "10", "5", "11"]
+	this.hourIndexMap = ["", 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 11, 0];
+	// minutes are ordered as follows: ["00", "30", "05", "35", "10", "40", "15", "45", "20", "50", "25", "55"]
+	this.minuteIndexMap = [0, 2, 4, 6, 8, 10, 1, 3, 5, 7, 9, 11];
+
+	this.templatePath =  dojo.uri.dojoUri("src/widget/templates/HtmlTimePicker.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlTimePicker.css");
+
+	this.fillInTemplate = function(){
+		this.initData();
+		this.initUI();
+	}
+
+	this.initData = function() {
+		// FIXME: doesn't currently validate the time before trying to set it
+		// Determine the date/time from stored info, or by default don't 
+		//  have a set time
+		// FIXME: should normalize against whitespace on storedTime... for now 
+		// just a lame hack
+		if(this.storedTime.indexOf("T")!=-1 && this.storedTime.split("T")[1] && this.storedTime!=" " && this.storedTime.split("T")[1]!="any") {
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime(this.storedTime, this.useDefaultMinutes, this.selectedTime.anyTime);
+		} else if (this.useDefaultTime) {
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime("", this.useDefaultMinutes, this.selectedTime.anyTime);
+		} else {
+			this.selectedTime.anyTime = true;
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime("", 0, 1);
+		}
+	}
+
+	this.initUI = function() {
+		// set UI to match the currently selected time
+		if(!this.selectedTime.anyTime && this.time) {
+			var amPmHour = dojo.widget.TimePicker.util.toAmPmHour(this.time.getHours());
+			var hour = amPmHour[0];
+			var isAm = amPmHour[1];
+			var minute = this.time.getMinutes();
+			var minuteIndex = parseInt(minute/5);
+			this.onSetSelectedHour(this.hourIndexMap[hour]);
+			this.onSetSelectedMinute(this.minuteIndexMap[minuteIndex]);
+			this.onSetSelectedAmPm(isAm);
+		} else {
+			this.onSetSelectedAnyTime();
+		}
+	}
+
+	this.setDateTime = function(rfcDate) {
+		this.storedTime = rfcDate;
+	}
+	
+	this.onClearSelectedHour = function(evt) {
+		this.clearSelectedHour();
+	}
+
+	this.onClearSelectedMinute = function(evt) {
+		this.clearSelectedMinute();
+	}
+
+	this.onClearSelectedAmPm = function(evt) {
+		this.clearSelectedAmPm();
+	}
+
+	this.onClearSelectedAnyTime = function(evt) {
+		this.clearSelectedAnyTime();
+		if(this.selectedTime.anyTime) {
+			this.selectedTime.anyTime = false;
+			this.time = dojo.widget.TimePicker.util.fromRfcDateTime("", this.useDefaultMinutes);
+			this.initUI();
+		}
+	}
+
+	this.clearSelectedHour = function() {
+		var hourNodes = this.hourContainerNode.getElementsByTagName("td");
+		for (var i=0; i<hourNodes.length; i++) {
+			dojo.html.setClass(hourNodes.item(i), "");
+		}
+	}
+
+	this.clearSelectedMinute = function() {
+		var minuteNodes = this.minuteContainerNode.getElementsByTagName("td");
+		for (var i=0; i<minuteNodes.length; i++) {
+			dojo.html.setClass(minuteNodes.item(i), "");
+		}
+	}
+
+	this.clearSelectedAmPm = function() {
+		var amPmNodes = this.amPmContainerNode.getElementsByTagName("td");
+		for (var i=0; i<amPmNodes.length; i++) {
+			dojo.html.setClass(amPmNodes.item(i), "");
+		}
+	}
+
+	this.clearSelectedAnyTime = function() {
+		dojo.html.setClass(this.anyTimeContainerNode, "anyTimeContainer");
+	}
+
+	this.onSetSelectedHour = function(evt) {
+		this.onClearSelectedAnyTime();
+		this.onClearSelectedHour();
+		this.setSelectedHour(evt);
+		this.onSetTime();
+	}
+
+	this.setSelectedHour = function(evt) {
+		if(evt && evt.target) {
+			dojo.html.setClass(evt.target, this.classNames.selectedTime);
+			this.selectedTime["hour"] = evt.target.innerHTML;
+		} else if (!isNaN(evt)) {
+			var hourNodes = this.hourContainerNode.getElementsByTagName("td");
+			if(hourNodes.item(evt)) {
+				dojo.html.setClass(hourNodes.item(evt), this.classNames.selectedTime);
+				this.selectedTime["hour"] = hourNodes.item(evt).innerHTML;
+			}
+		}
+		this.selectedTime.anyTime = false;
+	}
+
+	this.onSetSelectedMinute = function(evt) {
+		this.onClearSelectedAnyTime();
+		this.onClearSelectedMinute();
+		this.setSelectedMinute(evt);
+		this.selectedTime.anyTime = false;
+		this.onSetTime();
+	}
+
+	this.setSelectedMinute = function(evt) {
+		if(evt && evt.target) {
+			dojo.html.setClass(evt.target, this.classNames.selectedTime);
+			this.selectedTime["minute"] = evt.target.innerHTML;
+		} else if (!isNaN(evt)) {
+			var minuteNodes = this.minuteContainerNode.getElementsByTagName("td");
+			if(minuteNodes.item(evt)) {
+				dojo.html.setClass(minuteNodes.item(evt), this.classNames.selectedTime);
+				this.selectedTime["minute"] = minuteNodes.item(evt).innerHTML;
+			}
+		}
+	}
+
+	this.onSetSelectedAmPm = function(evt) {
+		this.onClearSelectedAnyTime();
+		this.onClearSelectedAmPm();
+		this.setSelectedAmPm(evt);
+		this.selectedTime.anyTime = false;
+		this.onSetTime();
+	}
+
+	this.setSelectedAmPm = function(evt) {
+		if(evt && evt.target) {
+			dojo.html.setClass(evt.target, this.classNames.selectedTime);
+			this.selectedTime["amPm"] = evt.target.innerHTML;
+		} else {
+			evt = evt ? 0 : 1;
+			var amPmNodes = this.amPmContainerNode.getElementsByTagName("td");
+			if(amPmNodes.item(evt)) {
+				dojo.html.setClass(amPmNodes.item(evt), this.classNames.selectedTime);
+				this.selectedTime["amPm"] = amPmNodes.item(evt).innerHTML;
+			}
+		}
+	}
+
+	this.onSetSelectedAnyTime = function(evt) {
+		this.onClearSelectedHour();
+		this.onClearSelectedMinute();
+		this.onClearSelectedAmPm();
+		this.setSelectedAnyTime();
+		this.onSetTime();
+	}
+
+	this.setSelectedAnyTime = function(evt) {
+		this.selectedTime.anyTime = true;
+		dojo.html.setClass(this.anyTimeContainerNode, this.classNames.selectedTime + " " + "anyTimeContainer");
+	}
+
+	this.onClick = function(evt) {
+		dojo.event.browser.stopEvent(evt)
+	}
+
+	this.onSetTime = function() {
+		if(this.selectedTime.anyTime) {
+			this.time = new Date();
+			var tempDateTime = dojo.widget.TimePicker.util.toRfcDateTime(this.time);
+			this.setDateTime(tempDateTime.split("T")[0]);
+		} else {
+			var hour = 12;
+			var minute = 0;
+			var isAm = false;
+			if(this.selectedTime["hour"]) {
+				hour = parseInt(this.selectedTime["hour"], 10);
+			}
+			if(this.selectedTime["minute"]) {
+				minute = parseInt(this.selectedTime["minute"], 10);
+			}
+			if(this.selectedTime["amPm"]) {
+				isAm = (this.selectedTime["amPm"].toLowerCase() == "am");
+			}
+			this.time = new Date();
+			this.time.setHours(dojo.widget.TimePicker.util.fromAmPmHour(hour, isAm));
+			this.time.setMinutes(minute);
+			this.setDateTime(dojo.widget.TimePicker.util.toRfcDateTime(this.time));
+		}
+	}
+
+}
+dojo.inherits(dojo.widget.html.TimePicker, dojo.widget.HtmlWidget);

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TitlePane.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TitlePane.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TitlePane.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/TitlePane.js Mon May 22 16:10:12 2006
@@ -0,0 +1,70 @@
+/*
+	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.TitlePane");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.lfx.*");
+
+dojo.widget.html.TitlePane = function(){
+	dojo.widget.HtmlWidget.call(this);
+	this.widgetType = "TitlePane";
+
+	this.labelNode="";
+	this.labelNodeClass="";
+	this.containerNodeClass="";
+	this.label="";
+
+	this.open=true;
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/TitlePane.html");
+}
+
+dojo.inherits(dojo.widget.html.TitlePane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TitlePane, {
+	isContainer: true,
+	postCreate: function() {
+		if (this.label) {
+			this.labelNode.appendChild(document.createTextNode(this.label));
+		}
+
+		if (this.labelNodeClass) {
+			dojo.html.addClass(this.labelNode, this.labelNodeClass);
+		}	
+
+		if (this.containerNodeClass) {
+			dojo.html.addClass(this.containerNode, this.containerNodeClass);
+		}	
+
+		if (!this.open) {
+			dojo.lfx.wipeOut(this.containerNode,0).play();
+		}
+	},
+
+	onLabelClick: function() {
+		if (this.open) {
+			dojo.lfx.wipeOut(this.containerNode,250).play();
+			this.open=false;
+		}else {
+			dojo.lfx.wipeIn(this.containerNode,250).play();
+			this.open=true;
+		}
+	},
+
+	setContent: function(content) {
+		this.containerNode.innerHTML=content;
+	},
+
+	setLabel: function(label) {
+		this.labelNode.innerHTML=label;
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:TitlePane");

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Tooltip.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Tooltip.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Tooltip.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/Tooltip.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.html.Tooltip");
+dojo.require("dojo.widget.html.ContentPane");
+dojo.require("dojo.widget.Tooltip");
+dojo.require("dojo.uri");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.html.Tooltip = function(){
+	// mix in the tooltip properties
+	dojo.widget.Tooltip.call(this);
+	dojo.widget.html.ContentPane.call(this);
+}
+dojo.inherits(dojo.widget.html.Tooltip, dojo.widget.html.ContentPane);
+dojo.lang.extend(dojo.widget.html.Tooltip, {
+
+	// Constructor arguments (should these be in tooltip.js rather than html/tooltip.js???)
+	caption: "",
+	showDelay: 500,
+	hideDelay: 100,
+	connectId: "",
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTooltipTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTooltipTemplate.css"),
+
+	connectNode: null,
+
+	// Tooltip has the following possible states:
+	//   erased - nothing on screen
+	//   displaying - currently being faded in (partially displayed)
+	//   displayed - fully displayed
+	//   erasing - currently being faded out (partially erased)
+	state: "erased",
+
+	fillInTemplate: function(args, frag){
+		if(this.caption != ""){
+			this.domNode.appendChild(document.createTextNode(this.caption));
+		}
+		this.connectNode = dojo.byId(this.connectId);		
+		dojo.widget.html.Tooltip.superclass.fillInTemplate.call(this, args, frag);
+	},
+	
+	postCreate: function(args, frag){
+		// The domnode was appended to my parent widget's domnode, but the positioning
+		// only works if the domnode is a child of document.body
+		document.body.appendChild(this.domNode);
+
+		dojo.event.connect(this.connectNode, "onmouseover", this, "onMouseOver");
+		dojo.widget.html.Tooltip.superclass.postCreate.call(this, args, frag);
+	},
+	
+	onMouseOver: function(e) {
+		this.mouse = {x: e.pageX, y: e.pageY};
+
+		if(!this.showTimer){
+			this.showTimer = setTimeout(dojo.lang.hitch(this, "show"), this.showDelay);
+			dojo.event.connect(document.documentElement, "onmousemove", this, "onMouseMove");
+		}
+	},
+
+	onMouseMove: function(e) {
+		this.mouse = {x: e.pageX, y: e.pageY};
+
+		if(dojo.html.overElement(this.connectNode, e) || dojo.html.overElement(this.domNode, e)) {
+			// If the tooltip has been scheduled to be erased, cancel that timer
+			// since we are hovering over element/tooltip again
+			if(this.hideTimer) {
+				clearTimeout(this.hideTimer);
+				delete this.hideTimer;
+			}
+		} else {
+			// mouse has been moved off the element/tooltip
+			// note: can't use onMouseOut to detect this because the "explode" effect causes
+			// spurious onMouseOut/onMouseOver events (due to interference from outline)
+			if(this.showTimer){
+				clearTimeout(this.showTimer);
+				delete this.showTimer;
+			}
+			if((this.state=="displaying"||this.state=="displayed") && !this.hideTimer){
+				this.hideTimer = setTimeout(dojo.lang.hitch(this, "hide"), this.hideDelay);
+			}
+		}
+	},
+
+	show: function() {
+		if(this.state=="erasing"){
+			// we are in the process of erasing; when that is finished, display it.
+			this.displayScheduled=true;
+			return;
+		}
+		if ( this.state=="displaying" || this.state=="displayed" ) { return; }
+
+		// prevent IE bleed through (iframe creation is deferred until first show()
+		// call because apparently it takes a long time)
+		if(!this.bgIframe){
+			this.bgIframe = new dojo.html.BackgroundIframe(this.domNode);
+		}
+
+		this.position();
+
+		// if rendering using explosion effect, need to set explosion source
+		this.explodeSrc = [this.mouse.x, this.mouse.y];
+
+		this.state="displaying";
+
+		dojo.widget.html.Tooltip.superclass.show.call(this);
+	},
+
+	onShow: function() {
+		dojo.widget.html.Tooltip.superclass.onShow.call(this);
+		
+		this.state="displayed";
+		
+		// in the corner case where the user has moved his mouse away
+		// while the tip was fading in
+		if(this.eraseScheduled){
+			this.hide();
+			this.eraseScheduled=false;
+		}
+	},
+
+	hide: function() {
+		if(this.state=="displaying"){
+			// in the process of fading in.  wait until that is finished and then fade out
+			this.eraseScheduled=true;
+			return;
+		}
+		if ( this.state=="displayed" ) {
+			this.state="erasing";
+			if ( this.showTimer ) {
+				clearTimeout(this.showTimer);
+				delete this.showTimer;
+			}
+			if ( this.hideTimer ) {
+				clearTimeout(this.hideTimer);
+				delete this.hideTimer;
+			}
+			dojo.event.disconnect(document.documentElement, "onmousemove", this, "onMouseMove");
+			dojo.widget.html.Tooltip.superclass.hide.call(this);
+		}
+	},
+
+	onHide: function(){
+		this.state="erased";
+
+		// in the corner case where the user has moved his mouse back
+		// while the tip was fading out
+		if(this.displayScheduled){
+			this.display();
+			this.displayScheduled=false;
+		}
+	},
+
+	position: function(){
+		dojo.html.placeOnScreenPoint(this.domNode, this.mouse.x, this.mouse.y, [10,15], true);
+		this.bgIframe.onResized();
+	},
+
+	onLoad: function(){
+		if(this.isShowing()){
+			// the tooltip has changed size due to downloaded contents, so reposition it
+			dojo.lang.setTimeout(this, this.position, 50);
+			dojo.widget.html.Tooltip.superclass.onLoad.apply(this, arguments);
+		}
+	},
+
+	onParentResized: function() {
+		// onParentResized() is called when the user has resized the browser window,
+		// but that doesn't affect this widget (or this widget's children)
+		// so it can be safely ignored
+	}
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/YahooMap.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/YahooMap.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/YahooMap.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/YahooMap.js Mon May 22 16:10:12 2006
@@ -0,0 +1,180 @@
+/*
+	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.YahooMap");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.math");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.YahooMap");
+
+(function(){
+	var yappid = djConfig["yAppId"]||djConfig["yahooAppId"]||"dojotoolkit";
+	if(!dojo.hostenv.post_load_){
+		if(yappid == "dojotoolkit"){
+			dojo.debug("please provide a unique Yahoo App ID in djConfig.yahooAppId when using the map widget");
+		}
+		var tag = "<scr"+"ipt src='http://api.maps.yahoo.com/ajaxymap?v=3.0&appid="+yappid+"'></scri"+"pt>";
+		if(!dj_global["YMap"]){
+			document.write(tag);
+		}
+	}else{
+		dojo.debug("cannot initialize map system after the page has been loaded! Please either manually include the script block provided by Yahoo in your page or require() the YahooMap widget before onload has fired");
+	}
+})();
+
+dojo.widget.html.YahooMap=function(){
+	dojo.widget.HtmlWidget.call(this);
+	dojo.widget.YahooMap.call(this);
+
+	this.map=null;
+	this.datasrc="";
+	this.data=[];
+	this.width=0;
+	this.height=0;
+	this.controls=["zoomlong","maptype","pan"];
+};
+dojo.inherits(dojo.widget.html.YahooMap, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.YahooMap, {
+	widgetType: "YahooMap",
+	templatePath:null,
+	templateCssPath:null,
+
+	findCenter:function(aPts){
+		var start=new YGeoPoint(37,-90);
+		if(aPts.length==0) return start;
+		var minLat,maxLat, minLon, maxLon, cLat, cLon;
+		minLat=maxLat=aPts[0].Lat;
+		minLon=maxLon=aPts[0].Lon;
+		for(var i=0; i<aPts.length; i++){
+			minLat=Math.min(minLat,aPts[i].Lat);
+			maxLat=Math.max(maxLat,aPts[i].Lat);
+			minLon=Math.min(minLon,aPts[i].Lon);
+			maxLon=Math.max(maxLon,aPts[i].Lon);
+		}
+		cLat=dojo.math.round((minLat+maxLat)/2,6);
+		cLon=dojo.math.round((minLon+maxLon)/2,6);
+		return new YGeoPoint(cLat,cLon);
+	},
+	setControls:function(){
+		var c=this.controls;
+		var t=dojo.widget.YahooMap.Controls;
+		for(var i=0; i<c.length; i++){
+			switch(c[i]){
+				case t.MapType:{
+					this.map.addTypeControl();
+					break;
+				}
+				case t.Pan:{
+					this.map.addPanControl();
+					break;
+				}
+				case t.ZoomLong:{
+					this.map.addZoomLong();
+					break;
+				}
+				case t.ZoomShort:{
+					this.map.addZoomShort();
+					break;
+				}
+			}
+		}
+	},
+	
+	parse:function(table){
+		this.data=[];
+
+		//	get the column indices
+		var h=table.getElementsByTagName("thead")[0];
+		if(!h){
+			return;
+		}
+
+		var a=[];
+		var cols=h.getElementsByTagName("td");
+		if(cols.length==0){
+			cols=h.getElementsByTagName("th");
+		}
+		for(var i=0; i<cols.length; i++){
+			var c=cols[i].innerHTML.toLowerCase();
+			if(c=="long") c="lng";
+			a.push(c);
+		}
+		
+		//	parse the data
+		var b=table.getElementsByTagName("tbody")[0];
+		if(!b){
+			return;
+		}
+		for(var i=0; i<b.childNodes.length; i++){
+			if(!(b.childNodes[i].nodeName&&b.childNodes[i].nodeName.toLowerCase()=="tr")){
+				continue;
+			}
+			var cells=b.childNodes[i].getElementsByTagName("td");
+			var o={};
+			for(var j=0; j<a.length; j++){
+				var col=a[j];
+				if(col=="lat"||col=="lng"){
+					o[col]=parseFloat(cells[j].innerHTML);					
+				}else{
+					o[col]=cells[j].innerHTML;
+				}
+			}
+			this.data.push(o);
+		}
+	},
+	render:function(){
+		var pts=[];
+		var d=this.data;
+		for(var i=0; i<d.length; i++){
+			var pt=new YGeoPoint(d[i].lat, d[i].lng);
+			pts.push(pt);
+			var icon=d[i].icon||null;
+			if(icon){
+				icon=new YImage(icon);
+			}
+			var m=new YMarker(pt,icon);
+			if(d[i].description){
+				m.addAutoExpand("<div>"+d[i].description+"</div>");
+			}
+			this.map.addOverlay(m);
+		}
+		var c=this.findCenter(pts);
+		var z=this.map.getZoomLevel(pts);
+		this.map.drawZoomAndCenter(c,z);
+	},
+	
+	initialize:function(args, frag){
+		if(!YMap || !YGeoPoint){
+			dojo.raise("dojo.widget.YahooMap: The Yahoo Map script must be included in order to use this widget.");
+		}
+		if(this.datasrc){
+			this.parse(dojo.byId(this.datasrc));
+		}
+		else if(this.domNode.getElementsByTagName("table")[0]){
+			this.parse(this.domNode.getElementsByTagName("table")[0]);
+		}
+	},
+	postCreate:function(){
+		//	clean the domNode before creating the map.
+		while(this.domNode.childNodes.length>0){
+			this.domNode.removeChild(this.domNode.childNodes[0]);
+		}
+
+		if(this.width>0&&this.height>0){
+			this.map=new YMap(this.domNode, YAHOO_MAP_REG, new YSize(this.width, this.height));
+		}else{
+			this.map=new YMap(this.domNode);
+		}
+		this.setControls();
+		this.render();
+	}
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/stabile.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/stabile.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/stabile.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/html/stabile.js Mon May 22 16:10:12 2006
@@ -0,0 +1,213 @@
+/*
+	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
+*/
+
+// Maintain state of widgets when user hits back/forward button
+
+dojo.provide("dojo.widget.html.stabile");
+
+dojo.widget.html.stabile = {
+	// Characters to quote in single-quoted regexprs
+	_sqQuotables: new RegExp("([\\\\'])", "g"),
+
+	// Current depth.
+	_depth: 0,
+
+	// Set to true when calling v.toString, to sniff for infinite
+	// recursion.
+	_recur: false,
+
+	// Levels of nesting of Array and object displays.
+	// If when >= depth, no display or array or object internals.
+	depthLimit: 2
+};
+
+
+
+
+
+//// PUBLIC METHODS
+
+// Get the state stored for the widget with the given ID, or undefined
+// if none.
+// 
+dojo.widget.html.stabile.getState = function(id){
+	dojo.widget.html.stabile.setup();
+	return dojo.widget.html.stabile.widgetState[id];
+}
+
+
+// Set the state stored for the widget with the given ID.  If isCommit
+// is true, commits all widget state to more stable storage.
+// 
+dojo.widget.html.stabile.setState = function(id, state, isCommit){
+	dojo.widget.html.stabile.setup();
+	dojo.widget.html.stabile.widgetState[id] = state;
+	if(isCommit){
+		dojo.widget.html.stabile.commit(dojo.widget.html.stabile.widgetState);
+	}
+}
+
+
+// Sets up widgetState: a hash keyed by widgetId, maps to an object
+// or array writable with "describe".  If there is data in the widget
+// storage area, use it, otherwise initialize an empty object.
+// 
+dojo.widget.html.stabile.setup = function(){
+	if(!dojo.widget.html.stabile.widgetState){
+		var text = dojo.widget.html.stabile.getStorage().value;
+		dojo.widget.html.stabile.widgetState = text ? dj_eval("("+text+")") : {};
+	}
+}
+
+
+// Commits all widget state to more stable storage, so if the user
+// navigates away and returns, it can be restored.
+// 
+dojo.widget.html.stabile.commit = function(state){
+	dojo.widget.html.stabile.getStorage().value = dojo.widget.html.stabile.description(state);
+}
+
+// Return a JSON "description string" for the given value.
+// Supports only core JavaScript types with literals, plus Date,
+// and cyclic structures are unsupported.
+// showAll defaults to false -- if true, this becomes a simple symbolic
+// object dumper, but you cannot "eval" the output.
+//
+dojo.widget.html.stabile.description = function(v, showAll){
+	// Save and later restore dojo.widget.html.stabile._depth;
+	var depth = dojo.widget.html.stabile._depth;
+
+	var describeThis = function() {
+		 return this.description(this, true);
+	} 
+	
+	try {
+
+		if(v===void(0)){
+			return "undefined";
+		}
+		if(v===null){
+			return "null";
+		}
+		if(typeof(v)=="boolean" || typeof(v)=="number"
+		    || v instanceof Boolean || v instanceof Number){
+			return v.toString();
+		}
+
+		if(typeof(v)=="string" || v instanceof String){
+			// Quote strings and their contents as required.
+			// Replacing by $& fails in IE 5.0
+			var v1 = v.replace(dojo.widget.html.stabile._sqQuotables, "\\$1"); 
+			v1 = v1.replace(/\n/g, "\\n");
+			v1 = v1.replace(/\r/g, "\\r");
+			// Any other important special cases?
+			return "'"+v1+"'";
+		}
+
+		if(v instanceof Date){
+			// Create a data constructor.
+			return "new Date("+d.getFullYear+","+d.getMonth()+","+d.getDate()+")";
+		}
+
+		var d;
+		if(v instanceof Array || v.push){
+			// "push" test needed for KHTML/Safari, don't know why -cp
+
+			if(depth>=dojo.widget.html.stabile.depthLimit)
+			  return "[ ... ]";
+
+			d = "[";
+			var first = true;
+			dojo.widget.html.stabile._depth++;
+			for(var i=0; i<v.length; i++){
+				// Skip functions and undefined values
+				// if(v[i]==undef || typeof(v[i])=="function")
+				//   continue;
+				if(first){
+					first = false;
+				}else{
+					d += ",";
+				}
+				d+=arguments.callee(v[i], showAll);
+			}
+			return d+"]";
+		}
+
+		if(v.constructor==Object
+		    || v.toString==describeThis){
+			if(depth>=dojo.widget.html.stabile.depthLimit)
+			  return "{ ... }";
+
+			// Instanceof Hash is good, or if we just use Objects,
+			// we can say v.constructor==Object.
+			// IE (5?) lacks hasOwnProperty, but perhaps objects do not always
+			// have prototypes??
+			if(typeof(v.hasOwnProperty)!="function" && v.prototype){
+				throw new Error("description: "+v+" not supported by script engine");
+			}
+			var first = true;
+			d = "{";
+			dojo.widget.html.stabile._depth++;
+			for(var key in v){
+				// Skip values that are functions or undefined.
+				if(v[key]==void(0) || typeof(v[key])=="function")
+					continue;
+				if(first){
+					first = false;
+				}else{
+					d += ", ";
+				}
+				var kd = key;
+				// If the key is not a legal identifier, use its description.
+				// For strings this will quote the stirng.
+				if(!kd.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)){
+					kd = arguments.callee(key, showAll);
+				}
+				d += kd+": "+arguments.callee(v[key], showAll);
+			}
+			return d+"}";
+		}
+
+		if(showAll){
+			if(dojo.widget.html.stabile._recur){
+				// Save the original definitions of toString;
+				var objectToString = Object.prototype.toString;
+				return objectToString.apply(v, []);
+			}else{
+				dojo.widget.html.stabile._recur = true;
+				return v.toString();
+			}
+		}else{
+			// log("Description? "+v.toString()+", "+typeof(v));
+			throw new Error("Unknown type: "+v);
+			return "'unknown'";
+		}
+
+	} finally {
+		// Always restore the global current depth.
+		dojo.widget.html.stabile._depth = depth;
+	}
+
+}
+
+
+
+//// PRIVATE TO MODULE
+
+// Gets an object (form field) with a read/write "value" property.
+// 
+dojo.widget.html.stabile.getStorage = function(){
+	if (dojo.widget.html.stabile.dataField) {
+		return dojo.widget.html.stabile.dataField;
+	}
+	var form = document.forms._dojo_form;
+	return dojo.widget.html.stabile.dataField = form ? form.stabile : {value: ""};
+}
+

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/Chart.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/Chart.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/Chart.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/Chart.js Mon May 22 16:10:12 2006
@@ -0,0 +1,531 @@
+/*
+	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.svg.Chart");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.Chart");
+dojo.require("dojo.math");
+dojo.require("dojo.html");
+dojo.require("dojo.svg");
+dojo.require("dojo.graphics.color");
+
+dojo.widget.svg.Chart=function(){
+	dojo.widget.Chart.call(this);
+	dojo.widget.HtmlWidget.call(this);
+};
+dojo.inherits(dojo.widget.svg.Chart, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.svg.Chart, {
+	//	widget props
+	templatePath:null,
+	templateCssPath:null,
+
+	//	state
+	_isInitialized:false,
+	hasData:false,
+
+	//	chart props
+	vectorNode:null,
+	plotArea:null,
+	dataGroup:null,
+	axisGroup:null,
+
+	properties:{
+		height:400,	//	defaults, will resize to the domNode.
+		width:600,
+		plotType:null,
+		padding:{
+			top:10,
+			bottom:2,
+			left:60,
+			right:30
+		},
+		axes:{
+			x:{
+				plotAt:0,
+				label:"",
+				unitLabel:"",
+				unitType:Number,
+				nUnitsToShow:10,
+				range:{
+					min:0,
+					max:200
+				}
+			},
+			y:{
+				plotAt:0,
+				label:"",
+				unitLabel:"",
+				unitType:Number,
+				nUnitsToShow:10,
+				range:{
+					min:0,
+					max:200
+				}
+			}
+		}
+	},
+	
+	fillInTemplate:function(args,frag){
+		this.initialize();
+		this.render();
+	},
+	parseData:function(){
+	},
+	initialize:function(){
+		this.parseData();
+	
+		//	begin by grabbing the table, and reading it in.
+		var table=this.domNode.getElementsByTagName("table")[0];
+		if (!table) return;
+		
+		var bRangeX=false;
+		var bRangeY=false;
+		
+		//	properties off the table
+		if (table.getAttribute("width")) this.properties.width=table.getAttribute("width");
+		if (table.getAttribute("height")) this.properties.height=table.getAttribute("height");
+		if (table.getAttribute("plotType")) this.properties.plotType=table.getAttribute("plotType");
+		if (table.getAttribute("padding")){
+			if (table.getAttribute("padding").indexOf(",") > -1)
+				var p=table.getAttribute("padding").split(","); 
+			else var p=table.getAttribute("padding").split(" ");
+			if (p.length==1){
+				var pad=parseFloat(p[0]);
+				this.properties.padding.top=pad;
+				this.properties.padding.right=pad;
+				this.properties.padding.bottom=pad;
+				this.properties.padding.left=pad;
+			} else if(p.length==2){
+				var padV=parseFloat(p[0]);
+				var padH=parseFloat(p[1]);
+				this.properties.padding.top=padV;
+				this.properties.padding.right=padH;
+				this.properties.padding.bottom=padV;
+				this.properties.padding.left=padH;
+			} else if(p.length==4){
+				this.properties.padding.top=parseFloat(p[0]);
+				this.properties.padding.right=parseFloat(p[1]);
+				this.properties.padding.bottom=parseFloat(p[2]);
+				this.properties.padding.left=parseFloat(p[3]);
+			}
+		}
+		if (table.getAttribute("rangeX")){
+			var p=table.getAttribute("rangeX");
+			if (p.indexOf(",")>-1) p=p.split(",");
+			else p=p.split(" ");
+			this.properties.axes.x.range.min=parseFloat(p[0]);
+			this.properties.axes.x.range.max=parseFloat(p[1]);
+			bRangeX=true;
+		}
+		if (table.getAttribute("rangeY")){
+			var p=table.getAttribute("rangeY");
+			if (p.indexOf(",")>-1) p=p.split(",");
+			else p=p.split(" ");
+			this.properties.axes.y.range.min=parseFloat(p[0]);
+			this.properties.axes.y.range.max=parseFloat(p[1]);
+			bRangeY=true;
+		}
+
+		var thead=table.getElementsByTagName("thead")[0];
+		var tbody=table.getElementsByTagName("tbody")[0];
+		if(!(thead&&tbody)) dojo.raise("dojo.widget.Chart: supplied table must define a head and a body.");
+
+		//	set up the series.
+		var columns=thead.getElementsByTagName("tr")[0].getElementsByTagName("th");	//	should be <tr><..>
+		
+		//	assume column 0 == X
+		for (var i=1; i<columns.length; i++){
+			var key="column"+i;
+			var label=columns[i].innerHTML;
+			var plotType=columns[i].getAttribute("plotType")||"line";
+			var color=columns[i].getAttribute("color");
+			var ds=new dojo.widget.Chart.DataSeries(key,label,plotType,color);
+			this.series.push(ds);
+		}
+
+		//	ok, get the values.
+		var rows=tbody.getElementsByTagName("tr");
+		var xMin=Number.MAX_VALUE,xMax=Number.MIN_VALUE;
+		var yMin=Number.MAX_VALUE,yMax=Number.MIN_VALUE;
+		var ignore = [
+			"accesskey","align","bgcolor","class",
+			"colspan","height","id","nowrap",
+			"rowspan","style","tabindex","title",
+			"valign","width"
+		];
+
+		for(var i=0; i<rows.length; i++){
+			var row=rows[i];
+			var cells=row.getElementsByTagName("td");
+			var x=Number.MIN_VALUE;
+			for (var j=0; j<cells.length; j++){
+				if (j==0){
+					x=parseFloat(cells[j].innerHTML);
+					xMin=Math.min(xMin, x);
+					xMax=Math.max(xMax, x);
+				} else {
+					var ds=this.series[j-1];
+					var y=parseFloat(cells[j].innerHTML);
+					yMin=Math.min(yMin,y);
+					yMax=Math.max(yMax,y);
+					var o={x:x, value:y};
+					var attrs=cells[j].attributes;
+					for(var k=0; k<attrs.length; k++){
+						var attr=attrs.item(k);
+						var bIgnore=false;
+						for (var l=0; l<ignore.length; l++){
+							if (attr.nodeName.toLowerCase()==ignore[l]){
+								bIgnore=true;
+								break;
+							}
+						}
+						if(!bIgnore) o[attr.nodeName]=attr.nodeValue;
+					}
+					ds.add(o);
+				}
+			}
+		}
+
+		//	fix the axes
+		if(!bRangeX){
+			this.properties.axes.x.range={min:xMin, max:xMax};
+		}
+		if(!bRangeY){
+			this.properties.axes.y.range={min:yMin, max:yMax};
+		}
+
+		//	where to plot the axes
+		if (table.getAttribute("axisAt")){
+			var p=table.getAttribute("axisAt");
+			if (p.indexOf(",")>-1) p=p.split(",");
+			else p=p.split(" ");
+			
+			//	x axis
+			if (!isNaN(parseFloat(p[0]))){
+				this.properties.axes.x.plotAt=parseFloat(p[0]);
+			} else if (p[0].toLowerCase()=="ymin"){
+				this.properties.axes.x.plotAt=this.properties.axes.y.range.min;
+			} else if (p[0].toLowerCase()=="ymax"){
+				this.properties.axes.x.plotAt=this.properties.axes.y.range.max;
+			}
+
+			// y axis
+			if (!isNaN(parseFloat(p[1]))){
+				this.properties.axes.y.plotAt=parseFloat(p[1]);
+			} else if (p[1].toLowerCase()=="xmin"){
+				this.properties.axes.y.plotAt=this.properties.axes.x.range.min;
+			} else if (p[1].toLowerCase()=="xmax"){
+				this.properties.axes.y.plotAt=this.properties.axes.x.range.max;
+			}
+		} else {
+			this.properties.axes.x.plotAt=this.properties.axes.y.range.min;
+			this.properties.axes.y.plotAt=this.properties.axes.x.range.min;
+		}
+
+		//	table values should be populated, now pop it off.
+		this.domNode.removeChild(table);
+
+		//	get the width and the height.
+//		this.properties.width=dojo.html.getInnerWidth(this.domNode);
+//		this.properties.height=dojo.html.getInnerHeight(this.domNode);
+
+		// ok, lets create the chart itself.
+		dojo.svg.g.suspend();		
+		if(this.vectorNode) this.destroy();
+		this.vectorNode=document.createElementNS(dojo.svg.xmlns.svg, "svg");
+		this.vectorNode.setAttribute("width", this.properties.width);
+		this.vectorNode.setAttribute("height", this.properties.height);
+
+		//	set up the clip path for the plot area.
+		var defs = document.createElementNS(dojo.svg.xmlns.svg, "defs");
+		var clip = document.createElementNS(dojo.svg.xmlns.svg, "clipPath");
+		clip.setAttribute("id","plotClip"+this.widgetId);
+		var rect = document.createElementNS(dojo.svg.xmlns.svg, "rect");		
+		rect.setAttribute("x", this.properties.padding.left);
+		rect.setAttribute("y", this.properties.padding.top);
+		rect.setAttribute("width", this.properties.width-this.properties.padding.left-this.properties.padding.right);
+		rect.setAttribute("height", this.properties.height-this.properties.padding.bottom-this.properties.padding.bottom);
+		clip.appendChild(rect);
+		defs.appendChild(clip);
+		this.vectorNode.appendChild(defs);
+
+		//	the plot background.
+		this.plotArea = document.createElementNS(dojo.svg.xmlns.svg, "g");
+		this.vectorNode.appendChild(this.plotArea);
+		var rect = document.createElementNS(dojo.svg.xmlns.svg, "rect");		
+		rect.setAttribute("x", this.properties.padding.left);
+		rect.setAttribute("y", this.properties.padding.top);
+		rect.setAttribute("width", this.properties.width-this.properties.padding.left-this.properties.padding.right);
+		rect.setAttribute("height", this.properties.height-this.properties.padding.bottom-this.properties.padding.bottom);
+		rect.setAttribute("fill", "#fff");
+		this.plotArea.appendChild(rect);
+
+		//	data group
+		this.dataGroup = document.createElementNS(dojo.svg.xmlns.svg, "g");
+		this.dataGroup.setAttribute("style","clip-path:url(#plotClip"+this.widgetId+");");
+		this.plotArea.appendChild(this.dataGroup);
+
+		//	axis group
+		this.axisGroup = document.createElementNS(dojo.svg.xmlns.svg, "g");
+		this.plotArea.appendChild(this.axisGroup);
+
+		//	x axis
+		var stroke=1;
+		var line = document.createElementNS(dojo.svg.xmlns.svg, "line");
+		var y=dojo.widget.svg.Chart.Plotter.getY(this.properties.axes.x.plotAt, this);
+		line.setAttribute("y1", y);
+		line.setAttribute("y2", y);
+		line.setAttribute("x1",this.properties.padding.left-stroke);
+		line.setAttribute("x2",this.properties.width-this.properties.padding.right);
+		line.setAttribute("style","stroke:#000;stroke-width:"+stroke+";");
+		this.axisGroup.appendChild(line);
+		
+		//	x axis units.
+		//	(min and max)
+		var textSize=10;
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.padding.left);
+		text.setAttribute("y", this.properties.height-this.properties.padding.bottom+textSize+2);
+		text.setAttribute("style", "text-anchor:middle;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.x.range.min),2)));
+		this.axisGroup.appendChild(text);
+		
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.width-this.properties.padding.right-(textSize/2));
+		text.setAttribute("y", this.properties.height-this.properties.padding.bottom+textSize+2);
+		text.setAttribute("style", "text-anchor:middle;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.x.range.max),2)));
+		this.axisGroup.appendChild(text);	
+		
+		//	y axis
+		var line=document.createElementNS(dojo.svg.xmlns.svg, "line");
+		var x=dojo.widget.svg.Chart.Plotter.getX(this.properties.axes.y.plotAt, this);
+		line.setAttribute("x1", x);
+		line.setAttribute("x2", x);
+		line.setAttribute("y1", this.properties.padding.top);
+		line.setAttribute("y2", this.properties.height-this.properties.padding.bottom);
+		line.setAttribute("style", "stroke:#000;stroke-width:"+stroke+";");
+		this.axisGroup.appendChild(line);
+
+		//	y axis units
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.padding.left-4);
+		text.setAttribute("y", this.properties.height-this.properties.padding.bottom);
+		text.setAttribute("style", "text-anchor:end;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.y.range.min),2)));
+		this.axisGroup.appendChild(text);
+		
+		var text = document.createElementNS(dojo.svg.xmlns.svg, "text");
+		text.setAttribute("x", this.properties.padding.left-4);
+		text.setAttribute("y", this.properties.padding.top+(textSize/2));
+		text.setAttribute("style", "text-anchor:end;font-size:"+textSize+"px;fill:#000;");
+		text.appendChild(document.createTextNode(dojo.math.round(parseFloat(this.properties.axes.y.range.max),2)));
+		this.axisGroup.appendChild(text);	
+
+		this.domNode.appendChild(this.vectorNode);
+		dojo.svg.g.resume();
+
+		//	this is last.
+		this.assignColors();
+		this._isInitialized=true;
+	},
+	destroy:function(){
+		while(this.domNode.childNodes.length>0){
+			this.domNode.removeChild(this.domNode.childNodes.item(0));
+		}
+		this.vectorNode=this.plotArea=this.dataGroup=this.axisGroup=null;
+	},
+	render:function(){
+		dojo.svg.g.suspend();
+		
+		if (this.dataGroup){
+			while(this.dataGroup.childNodes.length>0){
+				this.dataGroup.removeChild(this.dataGroup.childNodes.item(0));
+			}
+		} else {
+			this.initialize();
+		}
+
+		//	the remove/append is an attempt to streamline the rendering, it's totally optional
+//		var p=this.dataGroup.parentNode;
+//		p.removeChild(this.dataGroup);
+		for(var i=0; i<this.series.length; i++){
+			dojo.widget.svg.Chart.Plotter.plot(this.series[i], this);
+		}
+//		p.appendChild(this.dataGroup);
+		
+		dojo.svg.g.resume();
+	}
+});
+
+dojo.widget.svg.Chart.Plotter=new function(){
+	var _this=this;
+	var plotters = {};
+	var types=dojo.widget.Chart.PlotTypes;
+	
+	this.getX=function(value, chart){
+		var v=parseFloat(value);
+		var min=chart.properties.axes.x.range.min;
+		var max=chart.properties.axes.x.range.max;
+		var ofst=0-min;
+		min+=ofst; max+=ofst; v+=ofst;
+
+		var xmin=chart.properties.padding.left;
+		var xmax=chart.properties.width-chart.properties.padding.right;
+		var x=(v*((xmax-xmin)/max))+xmin;
+		return x;
+	};
+	this.getY=function(value, chart){
+		var v=parseFloat(value);
+		var max=chart.properties.axes.y.range.max;
+		var min=chart.properties.axes.y.range.min;
+		var ofst=0;
+		if(min<0)ofst+=Math.abs(min);
+		min+=ofst; max+=ofst; v+=ofst;
+		
+		var ymin=chart.properties.height-chart.properties.padding.bottom;
+		var ymax=chart.properties.padding.top;
+		var y=(((ymin-ymax)/(max-min))*(max-v))+ymax;
+		return y;
+	};
+
+	this.addPlotter=function(name, func){
+		plotters[name]=func;
+	};
+	this.plot=function(series, chart){
+		if (series.values.length==0) return;
+		if (series.plotType && plotters[series.plotType]){
+			return plotters[series.plotType](series, chart);
+		}
+		else if (chart.plotType && plotters[chart.plotType]){
+			return plotters[chart.plotType](series, chart);
+		}
+	};
+
+	//	plotting
+	plotters[types.Bar]=function(series, chart){
+		var space=1;
+		var lastW = 0;
+		for (var i=0; i<series.values.length; i++){
+			var x=_this.getX(series.values[i].x, chart);
+			var w;
+			if (i==series.values.length-1){
+				w=lastW;
+			} else{
+				w=_this.getX(series.values[i+1].x, chart)-x-space;
+				lastW=w;
+			}
+			x-=(w/2);
+
+			var yA=_this.getY(chart.properties.axes.x.plotAt, chart);
+			var y=_this.getY(series.values[i].value, chart);
+			var h=Math.abs(yA-y);
+			if (parseFloat(series.values[i].value)<chart.properties.axes.x.plotAt){
+				var oy=yA;
+				yA=y;
+				y=oy;
+			}
+
+			var bar=document.createElementNS(dojo.svg.xmlns.svg, "rect");
+			bar.setAttribute("fill", series.color);
+			bar.setAttribute("title", series.label + ": " + series.values[i].value);
+			bar.setAttribute("stroke-width", "0");
+			bar.setAttribute("x", x);
+			bar.setAttribute("y", y);
+			bar.setAttribute("width", w);
+			bar.setAttribute("height", h);
+			bar.setAttribute("fill-opacity", "0.9");
+			chart.dataGroup.appendChild(bar);
+		}
+	};
+	plotters[types.Line]=function(series, chart){
+		var tension=3;
+		var line = document.createElementNS(dojo.svg.xmlns.svg, "path");
+		line.setAttribute("fill", "none");
+		line.setAttribute("stroke", series.color);
+		line.setAttribute("stroke-width", "2");
+		line.setAttribute("stroke-opacity", "0.85");
+		line.setAttribute("title", series.label);
+		chart.dataGroup.appendChild(line);
+
+		var path = [];
+		for (var i=0; i<series.values.length; i++){
+			var x = _this.getX(series.values[i].x, chart)
+			var y = _this.getY(series.values[i].value, chart);
+
+			var dx = chart.properties.padding.left+1;
+			var dy = chart.properties.height-chart.properties.padding.bottom;
+			if (i>0){
+				dx=x-_this.getX(series.values[i-1].x, chart);
+				dy=_this.getY(series.values[i-1].value, chart);
+			}
+			
+			if (i==0) path.push("M");
+			else {
+				path.push("C");
+				var cx=x-(tension-1)*(dx/tension);
+				path.push(cx+","+dy);
+				cx=x-(dx/tension);
+				path.push(cx+","+y);
+			}
+			path.push(x+","+y);
+		}
+		line.setAttribute("d", path.join(" "));
+	};
+	plotters[types.Scatter]=function(series, chart){
+		var r=7;
+		for (var i=0; i<series.values.length; i++){
+			var x=_this.getX(series.values[i].x, chart);
+			var y=_this.getY(series.values[i].value, chart);
+			var point = document.createElementNS(dojo.svg.xmlns.svg, "path");
+			point.setAttribute("fill", series.color);
+			point.setAttribute("stroke-width", "0");
+			point.setAttribute("title", series.label + ": " + series.values[i].value);
+			point.setAttribute("d",
+				"M " + x + "," + (y-r) + " " +
+				"Q " + x + "," + y + " " + (x+r) + "," + y + " " +
+				"Q " + x + "," + y + " " + x + "," + (y+r) + " " +
+				"Q " + x + "," + y + " " + (x-r) + "," + y + " " +
+				"Q " + x + "," + y + " " + x + "," + (y-r) + " " +
+				"Z"
+			);
+			chart.dataGroup.appendChild(point);
+		}
+	};
+	plotters[types.Bubble]=function(series, chart){
+		//	added param for series[n].value: size
+		var minR=1;
+		
+		//	do this off the x axis?
+		var min=chart.properties.axes.x.range.min;
+		var max=chart.properties.axes.x.range.max;
+		var ofst=0-min;
+		min+=ofst; max+=ofst;
+
+		var xmin=chart.properties.padding.left;
+		var xmax=chart.properties.width-chart.properties.padding.right;
+		var factor=(max-min)/(xmax-xmin)*25;
+		
+		for (var i=0; i<series.values.length; i++){
+			var size = series.values[i].size;
+			if (isNaN(parseFloat(size))) size=minR;
+			var point=document.createElementNS(dojo.svg.xmlns.svg, "circle");
+			point.setAttribute("stroke-width", 0);
+			point.setAttribute("fill", series.color);
+			point.setAttribute("fill-opacity", "0.8");
+			point.setAttribute("r", (parseFloat(size)*factor)/2);
+			point.setAttribute("cx", _this.getX(series.values[i].x, chart));
+			point.setAttribute("cy", _this.getY(series.values[i].value, chart));
+			point.setAttribute("title", series.label + ": " + series.values[i].value + " (" + size + ")");
+			chart.dataGroup.appendChild(point);
+		}
+	};
+}();

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/HslColorPicker.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/HslColorPicker.js?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/HslColorPicker.js (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/svg/HslColorPicker.js Mon May 22 16:10:12 2006
@@ -0,0 +1,139 @@
+/*
+	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.svg.HslColorPicker");
+
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.HslColorPicker");
+dojo.require("dojo.math");
+dojo.require("dojo.svg");
+dojo.require("dojo.graphics.color");
+dojo.require("dojo.graphics.color.hsl");
+
+dojo.widget.svg.HslColorPicker=function(){
+	dojo.widget.HslColorPicker.call(this);
+	dojo.widget.HtmlWidget.call(this);
+	dojo.debug("warning: the HslColorPicker is not a finished widget, and is not yet ready for general use");
+	this.hue = "0";
+	this.saturation = "0";
+	this.light = "0";
+	this.storedColor = "#0054aa";
+	var _this = this;
+	this.filterObject = {
+	}
+};
+dojo.inherits(dojo.widget.svg.HslColorPicker, dojo.widget.HtmlWidget);
+dojo.lang.extend(dojo.widget.svg.HslColorPicker, {
+	//	widget props
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HslColorPicker.svg"),
+	fillInTemplate: function() {
+		this.height = "131px";
+		this.svgDoc = this.hueNode.ownerDocument;
+		this.leftGradientColorNode = this.hueNode.ownerDocument.getElementById("leftGradientColor");
+		this.rightGradientColorNode = this.hueNode.ownerDocument.getElementById("rightGradientColor");
+		this.hueNode.setAttributeNS(dojo.dom.xmlns.xlink, "href", dojo.uri.dojoUri("src/widget/templates/images/hue.png"));
+		var hsl = dojo.graphics.color.hex2hsl(this.storedColor);
+		this.hue = hsl[0];
+		this.saturation = hsl[1];
+		this.light = hsl[2];
+		this.setSaturationStopColors();
+		//this.setHueSlider();
+		//this.setSaturationLightSlider();
+	},
+	setSaturationStopColors: function() {
+		//this.leftGradientStopColor = "rgb(" + dojo.graphics.color.hsl2rgb(this.hue, 20, 50).join(", ") + ")";
+		//this.rightGradientStopColor = "rgb(" + dojo.graphics.color.hsl2rgb(this.hue, 100, 50).join(", ") + ")";
+		//this.leftGradientStopColor = dojo.graphics.color.hsl2hex(this.hue, 20, 50);
+		//this.rightGradientStopColor = dojo.graphics.color.hsl2hex(this.hue, 100, 50);
+		this.leftGradientStopColor = dojo.graphics.color.rgb2hex(this.hsl2rgb(this.hue, 0, 50));
+		this.rightGradientStopColor = dojo.graphics.color.rgb2hex(this.hsl2rgb(this.hue, 100, 50));
+		this.leftGradientColorNode.setAttributeNS(null,'stop-color',this.leftGradientStopColor);
+		this.rightGradientColorNode.setAttributeNS(null,'stop-color',this.rightGradientStopColor);
+	},
+	setHue: function(hue) {
+		this.hue = hue;
+	},
+	setHueSlider: function() {
+		// FIXME: need to add some padding around the picker so you can see the slider at the top and bottom of the picker)
+		this.hueSliderNode.setAttribute("y", parseInt((this.hue/360) * parseInt(this.height) - 2) + "px" );
+	},
+	setSaturationLight: function(saturation, light) {
+		this.saturation = saturation;
+		this.light = light;
+	},
+	setSaturationLightSlider: function() {
+		// TODO
+	},
+	onHueClick: function(evt) {
+		// get the position that was clicked on the element
+		// FIXME: handle document scrolling, offset
+		var yPosition = parseInt(evt.clientY) - parseInt(evt.target.getAttribute("y"));
+		this.setHue( 360 - parseInt(yPosition*(360/parseInt(this.height))) );
+		this.setSaturationStopColors();
+		this.setStoredColor(dojo.graphics.color.hsl2hex(this.hue, this.saturation, this.light));
+	},
+	onHueDrag: function(evt) {
+		// TODO
+	},
+	onSaturationLightClick: function(evt) {
+		// get the position that was clicked on the element
+		// FIXME: handle document scrolling, offset
+		var xPosition = parseInt(evt.clientX) - parseInt(evt.target.getAttribute("y"));
+		var yPosition = parseInt(evt.clientY) - parseInt(evt.target.getAttribute("y"));
+		var saturation = parseInt(parseInt(xPosition)*(101/106));
+		var light = parseInt(parseInt(yPosition)*(101/106));
+		this.setSaturationLight(saturation, light);
+		this.setStoredColor(dojo.graphics.color.hsl2hex(this.hue, this.saturation, this.light));
+	},
+	onSaturationLightDrag: function(evt) {
+		// TODO
+	},
+	getStoredColor: function() {
+		return this.storedColor;
+	},
+	setStoredColor: function(rgbHexColor) {
+		this.storedColor = rgbHexColor;
+		dojo.event.topic.publish("/" + this.widgetId + "/setStoredColor", this.filterObject);
+	},
+	hsl2rgb: function(hue, saturation, light)
+	{
+		// hsl2rgb in dojo.graphics.color did not behave hte way I expected, so 
+		// I'm using some old code I wrote until I figure out what the issue is
+		// first, check to see if saturation = 0
+		function rgb(q1,q2,hue) {
+			if (hue>360) hue=hue-360;
+			if (hue<0) hue=hue+360;
+			if (hue<60) return (q1+(q2-q1)*hue/60);
+			else if (hue<180) return(q2);
+			else if (hue<240) return(q1+(q2-q1)*(240-hue)/60);
+			else return(q1);
+		}
+		this.rgb = rgb
+	
+		if (saturation==0) {
+			return [Math.round(light*255/100), Math.round(light*255/100), Math.round(light*255/100)];
+		} else {
+			light = light/100;
+			saturation = saturation/100;
+			// check to see if light > 0.5
+			if ((light)<0.5) {
+				var temp2 = (light)*(1.0+saturation)
+			} else {
+				var temp2 = (light+saturation-(light*saturation))
+			}
+			var temp1 = 2.0*light - temp2;
+			var rgbcolor = [];
+			rgbcolor[0] = Math.round(rgb(temp1,temp2,parseInt(hue)+120)*255);
+			rgbcolor[1] = Math.round(rgb(temp1,temp2,hue)*255);
+			rgbcolor[2] = Math.round(rgb(temp1,temp2,parseInt(hue)-120)*255);
+			return rgbcolor;
+		}
+	}
+});

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/DemoEngine.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/DemoEngine.html?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/DemoEngine.html (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/DemoEngine.html Mon May 22 16:10:12 2006
@@ -0,0 +1,24 @@
+<div dojoAttachPoint="domNode">
+	<div dojoAttachPoint="navigationNode">
+		<table border="0" cellspacing="0" cellpadding="0">
+			<tr>
+				<td width="1%" valign="top" class="navigationCell"><h1>Categories</h1><div dojoAttachPoint="menuNavigationNode"></div></td>
+				<td width="99%" valign="top">
+					<div dojoAttachPoint="demoNavigationNode">
+					</div>
+				</td>
+			</tr>
+		</table>
+	</div>
+
+	<div dojoAttachPoint="demoContainerNode">
+
+		<div dojoAttachPoint="demoPaneNode">
+		</div>
+
+		<div dojoAttachPoint="demoHeaderNode">
+			<span dojoAttachPoint="collapsedMenuNode" dojoAttachEvent="onclick: expandDemoNavigation"></span>
+			<div dojoAttachPoint="aboutNode">About this Demo</div>
+		</div>
+	</div>
+</div>

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HslColorPicker.svg
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HslColorPicker.svg?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HslColorPicker.svg (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HslColorPicker.svg Mon May 22 16:10:12 2006
@@ -0,0 +1,30 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+	xmlns:xlink="http://www.w3.org/1999/xlink"
+	version="1.1" baseProfile="full" width="170" height="131" xmlns:html="http://www.w3.org/1999/xhtml">
+	<defs>
+	<linearGradient id="colorGradient" dojoAttachPoint="colorGradientNode" x1="0" x2="131" y1="0" y2="0" gradientUnits="userSpaceOnUse">
+		<stop id="leftGradientColor" dojoAttachPoint="leftGradientColorNode" offset="0%" stop-color="#828282"/>
+		<stop id="rightGradientColor" dojoAttachPoint="rightGradientColorNode" offset="100%" stop-color="#053fff"/>
+	</linearGradient>
+	<linearGradient id="verticalGradient" x1="0" x2="0" y1="0" y2="131" gradientUnits="userSpaceOnUse">
+		<stop offset="0%" style="stop-color:#000000;"/>
+		<stop offset="50%" style="stop-color:#000000;stop-opacity:0;"/>
+		<stop offset="50%" style="stop-color:#ffffff;stop-opacity:0;"/>
+		<stop offset="100%" style="stop-color:#ffffff;"/>
+	</linearGradient>
+	<linearGradient id="sliderGradient">
+		<stop offset="0%" style="stop-color:#000000;"/>
+		<stop offset="15%" style="stop-color:#ffffff;"/>
+		<stop offset="30%" style="stop-color:#000000;"/>
+		<stop offset="45%" style="stop-color:#ffffff;"/>
+		<stop offset="60%" style="stop-color:#000000;"/>
+		<stop offset="75%" style="stop-color:#ffffff;"/>
+		<stop offset="90%" style="stop-color:#000000;"/>
+	</linearGradient>
+</defs>
+	<rect x="0" y="0" width="131px" height="131px" fill="url(#colorGradient)"/>
+	<rect x="0" y="0" width="131px" height="131px" style="fill:url(#verticalGradient);"/>
+	<rect id="saturationLightSlider" dojoAttachPoint="saturationLightSliderNode" x="100" y="100" width="5px" height="5px" style="stroke:url(#sliderGradient);stroke-width:1px;fill-opacity:0;"/>
+	<image xlink:href="images/hue.png" dojoAttachPoint="hueNode" x="140px" y="0px" width="21px" height="131px" dojoAttachEvent="onclick: onHueClick;"/>
+	<rect dojoAttachPoint="hueSliderNode" x="139px" y="40px" width="24px" height="4px" style="stroke-opacity:1;fill-opacity:0;stroke:black;"/>
+</svg>

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.css
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.css?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.css (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.css Mon May 22 16:10:12 2006
@@ -0,0 +1,59 @@
+/* ---- button --- */
+.dojoButton {
+	padding: 0 0 0 0;
+	font-size: 8pt;
+	white-space: nowrap;
+	cursor: pointer;
+	font-family: Myriad, Tahoma, Verdana, sans-serif;
+}
+
+.dojoButton .dojoButtonContents {
+	padding: 2px 2px 2px 2px;
+	text-align: center;		/* if icon and label are split across two lines, center icon */
+	color: white;
+}
+
+.dojoButtonLeftPart .dojoButtonContents {
+	padding-right: 8px;
+}
+
+.dojoButtonDisabled {
+	cursor: url("images/no.gif"), default;
+}
+
+
+.dojoButtonContents img {
+	vertical-align: middle;	/* if icon and label are on same line, center them */
+}
+
+/* -------- colors ------------ */
+
+.dojoButtonHover .dojoButtonContents {
+}
+
+.dojoButtonDepressed .dojoButtonContents {
+	color: #293a4b;
+}
+
+.dojoButtonDisabled .dojoButtonContents {
+	color: #eeeeee;
+}
+
+
+/* ---------- drop down button specific ---------- */
+
+/* border between label and arrow (for drop down buttons */
+.dojoButton .border {
+	width: 1px;
+	background: gray;
+}
+
+/* button arrow */
+.dojoButton .downArrow {
+	padding-left: 10px;
+	text-align: center;
+}
+
+.dojoButton.disabled .downArrow {
+	cursor : default;
+}
\ No newline at end of file

Added: tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.html
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.html?rev=408783&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.html (added)
+++ tapestry/tapestry4/trunk/framework/src/js/dojo/src/widget/templates/HtmlButtonTemplate.html Mon May 22 16:10:12 2006
@@ -0,0 +1,6 @@
+<div class="dojoButton" style="position:relative;" dojoAttachEvent="onMouseOver; onMouseOut; onMouseDown; onMouseUp; onClick:buttonClick;">
+  <div class="dojoButtonContents" align=center dojoAttachPoint="containerNode" style="position:absolute;z-index:2;"></div>
+  <img dojoAttachPoint="leftImage" style="position:absolute;left:0px;">
+  <img dojoAttachPoint="centerImage" style="position:absolute;z-index:1;">
+  <img dojoAttachPoint="rightImage" style="position:absolute;top:0px;right:0px;">
+</div>