You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by he...@apache.org on 2006/11/13 23:55:14 UTC

svn commit: r474551 [34/49] - in /struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo: ./ src/ src/alg/ src/animation/ src/cal/ src/charting/ src/charting/svg/ src/charting/vml/ src/collections/ src/crypto/ src/data/ src/data/cs...

Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Editor2Toolbar.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FilteringTable.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FilteringTable.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FilteringTable.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FilteringTable.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,961 @@
+/*
+	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.FilteringTable");
+
+dojo.require("dojo.date.format");
+dojo.require("dojo.collections.Store");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.util");
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+dojo.widget.defineWidget(
+	"dojo.widget.FilteringTable", 
+	dojo.widget.HtmlWidget, 
+	function(){
+		//	summary
+		//	Initializes all properties for the widget.
+		this.store=new dojo.collections.Store();
+
+		//declare per instance changeable widget properties
+		this.valueField="Id";
+		this.multiple=false;
+		this.maxSelect=0;
+		this.maxSortable=1;  // how many columns can be sorted at once.
+		this.minRows=0;
+		this.defaultDateFormat = "%D";
+		this.isInitialized=false;
+		this.alternateRows=false;
+
+		this.columns=[];
+		this.sortInformation=[{
+			index:0,
+			direction:0
+		}];
+
+		// CSS definitions
+		this.headClass="";
+		this.tbodyClass="";
+		this.headerClass="";
+		this.headerUpClass="selectedUp";
+		this.headerDownClass="selectedDown";
+		this.rowClass="";
+		this.rowAlternateClass="alt";
+		this.rowSelectedClass="selected";
+		this.columnSelected="sorted-column";
+	},
+{
+	//	dojo widget properties
+	isContainer: false,
+	templatePath: null,
+	templateCssPath: null,
+
+	//	methods.
+	getTypeFromString: function(/* string */s){
+		//	summary
+		//	Gets a function based on the passed string.
+		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
+	},
+
+	//	custom data access.
+	getByRow: function(/*HTMLTableRow*/row){
+		//	summary
+		//	Returns the data object based on the passed row.
+		return this.store.getByKey(dojo.html.getAttribute(row, "value"));	//	object
+	},
+	getDataByRow: function(/*HTMLTableRow*/row){
+		//	summary
+		//	Returns the source data object based on the passed row.
+		return this.store.getDataByKey(dojo.html.getAttribute(row, "value")); // object
+	},
+
+	getRow: function(/* Object */ obj){
+		//	summary
+		//	Finds the row in the table based on the passed data object.
+		var rows = this.domNode.tBodies[0].rows;
+		for(var i=0; i<rows.length; i++){
+			if(this.store.getDataByKey(dojo.html.getAttribute(rows[i], "value")) == obj){
+				return rows[i];	//	HTMLTableRow
+			}
+		}
+		return null;	//	HTMLTableRow
+	},
+	getColumnIndex: function(/* string */fieldPath){
+		//	summary
+		//	Returns index of the column that represents the passed field path.
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == fieldPath){
+				return i;	//	integer
+			}
+		}
+		return -1;	//	integer
+	},
+
+	getSelectedData: function(){
+		//	summary
+		//	returns all objects that are selected.
+		var data=this.store.get();
+		var a=[];
+		for(var i=0; i<data.length; i++){
+			if(data[i].isSelected){
+				a.push(data[i].src);
+			}
+		}
+		if(this.multiple){
+			return a;		//	array
+		} else {
+			return a[0];	//	object
+		}
+	},
+	
+	isSelected: function(/* object */obj){
+		//	summary
+		//	Returns whether the passed object is currently selected.
+		var data = this.store.get();
+		for(var i=0; i<data.length; i++){
+			if(data[i].src == obj){
+				return true;	//	boolean
+			}
+		}
+		return false;	//	boolean
+	},
+	isValueSelected: function(/* string */val){
+		//	summary
+		//	Returns the object represented by key "val" is selected.
+		var v = this.store.getByKey(val);
+		if(v){
+			return v.isSelected;	//	boolean
+		}
+		return false;	//	boolean
+	},
+	isIndexSelected: function(/* number */idx){
+		//	summary
+		//	Returns the object represented by integer "idx" is selected.
+		var v = this.store.getByIndex(idx);
+		if(v){
+			return v.isSelected;	//	boolean
+		}
+		return false;	//	boolean
+	},
+	isRowSelected: function(/* HTMLTableRow */row){
+		//	summary
+		//	Returns if the passed row is selected.
+		var v = this.getByRow(row);
+		if(v){
+			return v.isSelected;	//	boolean
+		}
+		return false;	//	boolean
+	},
+
+	reset: function(){
+		//	summary
+		//	Resets the widget to its initial internal state.
+		this.store.clearData();
+		this.columns = [];
+		this.sortInformation = [ {index:0, direction:0} ];
+		this.resetSelections();
+		this.isInitialized = false;
+		this.onReset();
+	},
+	resetSelections: function(){
+		//	summary
+		//	Unselects all data objects.
+		this.store.forEach(function(element){
+			element.isSelected = false;
+		});
+	},
+	onReset:function(){ 
+		//	summary
+		//	Stub for onReset event.
+	},
+
+	//	selection and toggle functions
+	select: function(/*object*/ obj){
+		//	summary
+		//	selects the passed object.
+		var data = this.store.get();
+		for(var i=0; i<data.length; i++){
+			if(data[i].src == obj){
+				data[i].isSelected = true;
+				break;
+			}
+		}
+		this.onDataSelect(obj);
+	},
+	selectByValue: function(/*string*/ val){
+		//	summary
+		//	selects the object represented by key "val".
+		this.select(this.store.getDataByKey(val));
+	},
+	selectByIndex: function(/*number*/ idx){
+		//	summary
+		//	selects the object represented at index "idx".
+		this.select(this.store.getDataByIndex(idx));
+	},
+	selectByRow: function(/*HTMLTableRow*/ row){
+		//	summary
+		//	selects the object represented by HTMLTableRow row.
+		this.select(this.getDataByRow(row));
+	},
+	selectAll: function(){
+		//	summary
+		//	selects all objects.
+		this.store.forEach(function(element){
+			element.isSelected = true;
+		});
+	},
+	onDataSelect: function(/* object */obj){ 
+		//	summary
+		//	Stub for onDataSelect event.
+	},
+
+	toggleSelection: function(/*object*/obj){
+		//	summary
+		//	Flips the selection state of passed obj.
+		var data = this.store.get();
+		for(var i=0; i<data.length; i++){
+			if(data[i].src == obj){
+				data[i].isSelected = !data[i].isSelected;
+				break;
+			}
+		}
+		this.onDataToggle(obj);
+	},
+	toggleSelectionByValue: function(/*string*/val){
+		//	summary
+		//	Flips the selection state of object represented by val.
+		this.toggleSelection(this.store.getDataByKey(val));
+	},
+	toggleSelectionByIndex: function(/*number*/idx){
+		//	summary
+		//	Flips the selection state of object at index idx.
+		this.toggleSelection(this.store.getDataByIndex(idx));
+	},
+	toggleSelectionByRow: function(/*HTMLTableRow*/row){
+		//	summary
+		//	Flips the selection state of object represented by row.
+		this.toggleSelection(this.getDataByRow(row));
+	},
+	toggleAll: function(){
+		//	summary
+		//	Flips the selection state of all objects.
+		this.store.forEach(function(element){
+			element.isSelected = !element.isSelected;
+		});
+	},
+	onDataToggle: function(/* object */obj){ 
+		//	summary
+		//	Stub for onDataToggle event.
+	},
+
+	//	parsing functions, from HTML to metadata/SimpleStore
+	_meta:{
+		field:null,
+		format:null,
+		filterer:null,
+		noSort:false,
+		sortType:"String",
+		dataType:String,
+		sortFunction:null,
+		filterFunction:null,
+		label:null,
+		align:"left",
+		valign:"middle",
+		getField:function(){ 
+			return this.field || this.label; 
+		},
+		getType:function(){ 
+			return this.dataType; 
+		}
+	},
+	createMetaData: function(/* object */obj){
+		//	summary
+		//	Take a JSON-type structure and make it into a ducktyped metadata object.
+		for(var p in this._meta){
+			//	rudimentary mixin
+			if(!obj[p]){
+				obj[p] = this._meta[p];
+			}
+		}
+		if(!obj.label){
+			obj.label=obj.field;
+		}
+		if(!obj.filterFunction){
+			obj.filterFunction=this._defaultFilter;
+		}
+		return obj;	//	object
+	},
+	parseMetadata: function(/* HTMLTableHead */head){
+		//	summary
+		//	Parses the passed HTMLTableHead element to create meta data.
+		this.columns=[];
+		this.sortInformation=[];
+		var row = head.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 = this.createMetaData({ });
+			
+			//	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");
+			}
+			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;
+				}
+			}
+			o.label = dojo.html.renderedTextContent(cells[i]);
+			if(dojo.html.hasAttribute(cells[i], "field")){
+				o.field=dojo.html.getAttribute(cells[i],"field");
+			} else if(o.label.length > 0){
+				o.field=o.label;
+			} else {
+				o.field = "field" + i;
+			}
+			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__"
+				}else{
+					var type = this.getTypeFromString(sortType);
+					if(type){
+						o.sortType = sortType;
+						o.dataType = type;
+					}
+				}
+			}
+
+			//	TODO: set up filtering mechanisms here.
+			if(dojo.html.hasAttribute(cells[i], "filterusing")){
+				var trans = dojo.html.getAttribute(cells[i],"filterusing");
+				var f = this.getTypeFromString(trans);
+				if (f != null && f != window && typeof(f)=="function"){
+					o.filterFunction=f;
+				}
+			}
+			
+			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")){
+				var info = {
+					index:i,
+					direction:0
+				};
+				var dir = dojo.html.getAttribute(cells[i], "sort");
+				if(!isNaN(parseInt(dir))){
+					dir = parseInt(dir);
+					info.direction = (dir != 0) ? 1 : 0;
+				}else{
+					info.direction = (dir.toLowerCase() == "desc") ? 1 : 0;
+				}
+				this.sortInformation.push(info);
+			}
+		}
+		if(this.sortInformation.length == 0){
+			this.sortInformation.push({
+				index:0,
+				direction:0
+			});
+		} else if (this.sortInformation.length > this.maxSortable){
+			this.sortInformation.length = this.maxSortable;
+		}
+	},
+	parseData: function(/* HTMLTableBody */body){
+		//	summary
+		//	Parse HTML data into native JSON structure for the store.
+		if(body.rows.length == 0 && this.columns.length == 0){
+			return;	//	there's no data, ignore me.
+		}
+
+		//	create a data constructor based on what we've got for the fields.
+		var self=this;
+		this["__selected__"] = [];
+		var arr = this.store.getFromHtml(this.columns, body, function(obj, row){
+			obj[self.valueField] = dojo.html.getAttribute(row, "value");
+			if(dojo.html.getAttribute(row, "selected")=="true"){
+				self["__selected__"].push(obj);
+			}
+		});
+		this.store.setData(arr);
+		for(var i=0; i<this["__selected__"].length; i++){
+			this.select(this["__selected__"][i]);
+		}
+		this.renderSelections();
+
+		delete this["__selected__"];
+
+		//	say that we are already initialized so that we don't kill anything
+		this.isInitialized=true;
+	},
+
+	//	standard events
+	onSelect: function(/* HTMLEvent */e){
+		//	summary
+		//	Handles the onclick event of any element.
+		var row = dojo.html.getParentByType(e.target,"tr");
+		if(dojo.html.hasAttribute(row,"emptyRow")){
+			return;
+		}
+		var body = dojo.html.getParentByType(row,"tbody");
+		if(this.multiple){
+			if(e.shiftKey){
+				var startRow;
+				var rows=body.rows;
+				for(var i=0;i<rows.length;i++){
+					if(rows[i]==row){
+						break;
+					}
+					if(this.isRowSelected(rows[i])){
+						startRow=rows[i];
+					}
+				}
+				if(!startRow){
+					startRow = row;
+					for(; i<rows.length; i++){
+						if(this.isRowSelected(rows[i])){
+							row = rows[i];
+							break;
+						}
+					}
+				}
+				this.resetSelections();
+				if(startRow == row){
+					this.toggleSelectionByRow(row);
+				} else {
+					var doSelect = false;
+					for(var i=0; i<rows.length; i++){
+						if(rows[i] == startRow){
+							doSelect=true;
+						}
+						if(doSelect){
+							this.selectByRow(rows[i]);
+						}
+						if(rows[i] == row){
+							doSelect = false;
+						}
+					}
+				}
+			} else {
+				this.toggleSelectionByRow(row);
+			}
+		} else {
+			this.resetSelections();
+			this.toggleSelectionByRow(row);
+		}
+		this.renderSelections();
+	},
+	onSort: function(/* HTMLEvent */e){
+		//	summary
+		//	Sort the table based on the column selected.
+		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++){
+			dojo.html.setClass(headers[i], this.headerClass);
+			if(headers[i]==header){
+				if(this.sortInformation[0].index != i){
+					this.sortInformation.unshift({ 
+						index:i, 
+						direction:0
+					});
+				} else {
+					this.sortInformation[0] = {
+						index:i,
+						direction:(~this.sortInformation[0].direction)&1
+					};
+				}
+			}
+		}
+
+		this.sortInformation.length = Math.min(this.sortInformation.length, this.maxSortable);
+		for(var i=0; i<this.sortInformation.length; i++){
+			var idx=this.sortInformation[i].index;
+			var dir=(~this.sortInformation[i].direction)&1;
+			dojo.html.setClass(headers[idx], dir==0?this.headerDownClass:this.headerUpClass);
+		}
+		this.render();
+	},
+	onFilter: function(){
+		//	summary
+		//	show or hide rows based on the parameters of the passed filter.
+	},
+
+	//	Filtering methods
+	_defaultFilter: function(/* Object */obj){
+		//	summary
+		//	Always return true as the result of the default filter.
+		return true;
+	},
+	setFilter: function(/* string */field, /* function */fn){
+		//	summary
+		//	set a filtering function on the passed field.
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == field){
+				this.columns[i].filterFunction=fn;
+				break;
+			}
+		}
+		this.applyFilters();
+	},
+	setFilterByIndex: function(/* number */idx, /* function */fn){
+		//	summary
+		//	set a filtering function on the passed column index.
+		this.columns[idx].filterFunction=fn;
+		this.applyFilters();
+	},
+	clearFilter: function(/* string */field){
+		//	summary
+		//	clear a filtering function on the passed field.
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == field){
+				this.columns[i].filterFunction=this._defaultFilter;
+				break;
+			}
+		}
+		this.applyFilters();
+	}, 
+	clearFilterByIndex: function(/* number */idx){
+		//	summary
+		//	clear a filtering function on the passed column index.
+		this.columns[idx].filterFunction=this._defaultFilter;
+		this.applyFilters();
+	}, 
+	clearFilters: function(){
+		//	summary
+		//	clears all filters.
+		for(var i=0; i<this.columns.length; i++){
+			this.columns[i].filterFunction=this._defaultFilter;
+		}
+		//	we'll do the clear manually, it will be faster.
+		var rows=this.domNode.tBodies[0].rows;
+		for(var i=0; i<rows.length; i++){
+			rows[i].style.display="";
+			if(this.alternateRows){
+				dojo.html[((i % 2 == 1)?"addClass":"removeClass")](rows[i], this.rowAlternateClass);
+			}
+		}
+		this.onFilter();
+	},
+	applyFilters: function(){
+		//	summary
+		//	apply all filters to the table.
+		var alt=0;
+		var rows=this.domNode.tBodies[0].rows;
+		for(var i=0; i<rows.length; i++){
+			var b=true;
+			var row=rows[i];
+			for(var j=0; j<this.columns.length; j++){
+				var value = this.store.getField(this.getDataByRow(row), this.columns[j].getField());
+				if(this.columns[j].getType() == Date && value != null && !value.getYear){
+					value = new Date(value);
+				}
+				if(!this.columns[j].filterFunction(value)){
+					b=false;
+					break;
+				}
+			}
+			row.style.display=(b?"":"none");
+			if(b && this.alternateRows){
+				dojo.html[((alt++ % 2 == 1)?"addClass":"removeClass")](row, this.rowAlternateClass);
+			}
+		}
+		this.onFilter();
+	},
+
+	//	sorting functionality
+	createSorter: function(/* array */info){
+		//	summary
+		//	creates a custom function to be used for sorting.
+		var self=this;
+		var sortFunctions=[];	//	our function stack.
+	
+		function createSortFunction(fieldIndex, dir){
+			var meta=self.columns[fieldIndex];
+			var field=meta.getField();
+			return function(rowA, rowB){
+				if(dojo.html.hasAttribute(rowA,"emptyRow") || dojo.html.hasAttribute(rowB,"emptyRow")){
+					return -1;
+				}
+				//	TODO: check for markup and compare by rendered text.
+				var a = self.store.getField(self.getDataByRow(rowA), field);
+				var b = self.store.getField(self.getDataByRow(rowB), field);
+				var ret = 0;
+				if(a > b) ret = 1;
+				if(a < b) ret = -1;
+				return dir * ret;
+			}
+		}
+
+		var current=0;
+		var max = Math.min(info.length, this.maxSortable, this.columns.length);
+		while(current < max){
+			var direction = (info[current].direction == 0) ? 1 : -1;
+			sortFunctions.push(
+				createSortFunction(info[current].index, direction)
+			);
+			current++;
+		}
+
+		return function(rowA, rowB){
+			var idx=0;
+			while(idx < sortFunctions.length){
+				var ret = sortFunctions[idx++](rowA, rowB);
+				if(ret != 0) return ret;
+			}
+			//	if we got here then we must be equal.
+			return 0; 	
+		};	//	function
+	},
+
+	//	rendering
+	createRow: function(/* object */obj){
+		//	summary
+		//	Create an HTML row based on the passed object
+		var row=document.createElement("tr");
+		dojo.html.disableSelection(row);
+		if(obj.key != null){
+			row.setAttribute("value", obj.key);
+		}
+		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);
+			var val = this.store.getField(obj.src, this.columns[j].getField());
+			if(typeof(val)=="undefined"){
+				val="";
+			}
+			this.fillCell(cell, this.columns[j], val);
+			row.appendChild(cell);
+		}
+		return row;	//	HTMLTableRow
+	},
+	fillCell: function(/* HTMLTableCell */cell, /* object */meta, /* object */val){
+		//	summary
+		//	Fill the passed cell with value, based on the passed meta object.
+		if(meta.sortType=="__markup__"){
+			cell.innerHTML=val;
+		} else {
+			if(meta.getType()==Date) {
+				val=new Date(val);
+				if(!isNaN(val)){
+					var format = this.defaultDateFormat;
+					if(meta.format){
+						format = meta.format;
+					}
+					cell.innerHTML = dojo.date.strftime(val, format);
+				} else {
+					cell.innerHTML = val;
+				}
+			} else if ("Number number int Integer float Float".indexOf(meta.getType())>-1){
+				//	TODO: number formatting
+				if(val.length == 0){
+					val="0";
+				}
+				var n = parseFloat(val, 10) + "";
+				//	TODO: numeric formatting + rounding :)
+				if(n.indexOf(".")>-1){
+					n = dojo.math.round(parseFloat(val,10),2);
+				}
+				cell.innerHTML = n;
+			}else{
+				cell.innerHTML = val;
+			}
+		}
+	},
+	prefill: function(){
+		//	summary
+		//	if there's no data in the table, then prefill it with this.minRows.
+		this.isInitialized = false;
+		var body = this.domNode.tBodies[0];
+		while (body.childNodes.length > 0){
+			body.removeChild(body.childNodes[0]);
+		}
+		
+		if(this.minRows>0){
+			for(var i=0; i < this.minRows; i++){
+				var row = document.createElement("tr");
+				if(this.alternateRows){
+					dojo.html[((i % 2 == 1)?"addClass":"removeClass")](row, this.rowAlternateClass);
+				}
+				row.setAttribute("emptyRow","true");
+				for(var j=0; j<this.columns.length; j++){
+					var cell = document.createElement("td");
+					cell.innerHTML = "&nbsp;";
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+	},
+	init: function(){
+		//	summary
+		//	initializes the table of data
+		this.isInitialized=false;
+
+		//	if there is no thead, create it now.
+		var head=this.domNode.getElementsByTagName("thead")[0];
+		if(head.getElementsByTagName("tr").length == 0){
+			//	render the column code.
+			var row=document.createElement("tr");
+			for(var i=0; i<this.columns.length; i++){
+				var cell=document.createElement("td");
+				cell.setAttribute("align", this.columns[i].align);
+				cell.setAttribute("valign", this.columns[i].valign);
+				dojo.html.disableSelection(cell);
+				cell.innerHTML=this.columns[i].label;
+				row.appendChild(cell);
+
+				//	attach the events.
+				if(!this.columns[i].noSort){
+					dojo.event.connect(cell, "onclick", this, "onSort");
+				}
+			}
+			dojo.html.prependChild(row, head);
+		}
+		
+		if(this.store.get().length == 0){
+			return false;
+		}
+
+		var idx=this.domNode.tBodies[0].rows.length;
+		if(!idx || idx==0 || this.domNode.tBodies[0].rows[0].getAttribute("emptyRow")=="true"){
+			idx = 0;
+			var body = this.domNode.tBodies[0];
+			while(body.childNodes.length>0){
+				body.removeChild(body.childNodes[0]);
+			}
+
+			var data = this.store.get();
+			for(var i=0; i<data.length; i++){
+				var row = this.createRow(data[i]);
+				dojo.event.connect(row, "onclick", this, "onSelect");
+				body.appendChild(row);
+				idx++;
+			}
+		}
+
+		//	add empty rows
+		if(this.minRows > 0 && idx < this.minRows){
+			idx = this.minRows - idx;
+			for(var i=0; i<idx; i++){
+				row=document.createElement("tr");
+				row.setAttribute("emptyRow","true");
+				for(var j=0; j<this.columns.length; j++){
+					cell=document.createElement("td");
+					cell.innerHTML="&nbsp;";
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+
+		//	last but not least, show any columns that have sorting already on them.
+		var row=this.domNode.getElementsByTagName("thead")[0].rows[0];
+		var cellTag="td";
+		if(row.getElementsByTagName(cellTag).length==0) cellTag="th";
+		var headers=row.getElementsByTagName(cellTag);
+		for(var i=0; i<headers.length; i++){
+			dojo.html.setClass(headers[i], this.headerClass);
+		}
+		for(var i=0; i<this.sortInformation.length; i++){
+			var idx=this.sortInformation[i].index;
+			var dir=(~this.sortInformation[i].direction)&1;
+			dojo.html.setClass(headers[idx], dir==0?this.headerDownClass:this.headerUpClass);
+		}
+
+		this.isInitialized=true;
+		return this.isInitialized;
+	},
+	render: function(){
+		//	summary
+		//	Renders the actual table data.
+
+	/*	The method that should be called once underlying changes
+	 *	are made, including sorting, filtering, data changes.
+	 *	Rendering the selections themselves are a different method,
+	 *	which render() will call as the last step.
+	 ****************************************************************/
+		if(!this.isInitialized){
+			var b = this.init();
+			if(!b){
+				this.prefill();
+				return;
+			}
+		}
+		
+		//	do the sort
+		var rows=[];
+		var body=this.domNode.tBodies[0];
+		var emptyRowIdx=-1;
+		for(var i=0; i<body.rows.length; i++){
+			rows.push(body.rows[i]);
+		}
+
+		//	build the sorting function, and do the sorting.
+		var sortFunction = this.createSorter(this.sortInformation);
+		if(sortFunction){
+			rows.sort(sortFunction);
+		}
+
+		//	append the rows without killing them, this should help with the HTML problems.
+		for(var i=0; i<rows.length; i++){
+			if(this.alternateRows){
+				dojo.html[((i%2==1)?"addClass":"removeClass")](rows[i], this.rowAlternateClass);
+			}
+			dojo.html[(this.isRowSelected(body.rows[i])?"addClass":"removeClass")](body.rows[i], this.rowSelectedClass);
+			body.appendChild(rows[i]);
+		}
+	},
+	renderSelections: function(){
+		//	summary
+		//	Render all selected objects using CSS.
+		var body=this.domNode.tBodies[0];
+		for(var i=0; i<body.rows.length; i++){
+			dojo.html[(this.isRowSelected(body.rows[i])?"addClass":"removeClass")](body.rows[i], this.rowSelectedClass);
+		}
+	},
+
+	//	widget lifetime handlers
+	initialize: function(){ 
+		//	summary
+		//	Initializes the widget.
+		var self=this;
+		//	connect up binding listeners here.
+		dojo.event.connect(this.store, "onSetData", function(){
+			self.store.forEach(function(element){
+				element.isSelected = false;
+			});
+			self.isInitialized=false;
+			var body = self.domNode.tBodies[0];
+			if(body){
+				while(body.childNodes.length>0){
+					body.removeChild(body.childNodes[0]);
+				}
+			}
+			self.render();
+		});
+		dojo.event.connect(this.store, "onClearData", function(){
+			self.render();
+		});
+		dojo.event.connect(this.store, "onAddData", function(addedObject){
+			var row=self.createRow(addedObject);
+			dojo.event.connect(row, "onclick", self, "onSelect");
+			self.domNode.tBodies[0].appendChild(row);
+			self.render();
+		});
+		dojo.event.connect(this.store, "onAddDataRange", function(arr){
+			for(var i=0; i<arr.length; i++){
+				arr[i].isSelected=false;
+				var row=self.createRow(arr[i]);
+				dojo.event.connect(row, "onclick", self, "onSelect");
+				self.domNode.tBodies[0].appendChild(row);
+			};
+			self.render();
+		});
+		dojo.event.connect(this.store, "onRemoveData", function(removedObject){
+			var rows = self.domNode.tBodies[0].rows;
+			for(var i=0; i<rows.length; i++){
+				if(self.getDataByRow(rows[i]) == removedObject.src){
+					rows[i].parentNode.removeChild(rows[i]);
+					break;
+				}
+			}
+			self.render();
+		});
+		dojo.event.connect(this.store, "onUpdateField", function(obj, fieldPath, val){
+			var row = self.getRow(obj);
+			var idx = self.getColumnIndex(fieldPath);
+			if(row && row.cells[idx] && self.columns[idx]){
+				self.fillCell(row.cells[idx], self.columns[idx], val);
+			}
+		});
+	},
+	postCreate: function(){
+		//	summary
+		//	finish widget initialization.
+		this.store.keyField = this.valueField;
+
+		if(this.domNode){
+			//	start by making sure domNode is a table element;
+			if(this.domNode.nodeName.toLowerCase() != "table"){
+			}
+
+			//	see if there is columns set up already
+			if(this.domNode.getElementsByTagName("thead")[0]){
+				var head=this.domNode.getElementsByTagName("thead")[0];
+				if(this.headClass.length > 0){
+					head.className = this.headClass;
+				}
+				dojo.html.disableSelection(this.domNode);
+				this.parseMetadata(head);
+
+				var header="td";
+				if(head.getElementsByTagName(header).length==0){
+					header="th";
+				}
+				var headers = head.getElementsByTagName(header);
+				for(var i=0; i<headers.length; i++){
+					if(!this.columns[i].noSort){
+						dojo.event.connect(headers[i], "onclick", this, "onSort");
+					}
+				}
+			} else {
+				this.domNode.appendChild(document.createElement("thead"));
+			}
+
+			// if the table doesn't have a tbody already, add one and grab a reference to it
+			if (this.domNode.tBodies.length < 1) {
+				var body = document.createElement("tbody");
+				this.domNode.appendChild(body);
+			} else {
+				var body = this.domNode.tBodies[0];
+			}
+
+			if (this.tbodyClass.length > 0){
+				body.className = this.tbodyClass;
+			}
+			this.parseData(body);
+		}
+	}
+});

Propchange: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FilteringTable.js
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FisheyeList.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FisheyeList.js?view=diff&rev=474551&r1=474550&r2=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FisheyeList.js (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FisheyeList.js Mon Nov 13 14:54:45 2006
@@ -1,5 +1,5 @@
 /*
-	Copyright (c) 2004-2005, The Dojo Foundation
+	Copyright (c) 2004-2006, The Dojo Foundation
 	All Rights Reserved.
 
 	Licensed under the Academic Free License version 2.1 or above OR the
@@ -9,8 +9,6 @@
 */
 
 dojo.provide("dojo.widget.FisheyeList");
-dojo.provide("dojo.widget.html.FisheyeList");
-dojo.provide("dojo.widget.html.FisheyeListItem");
 
 //
 // TODO
@@ -20,76 +18,114 @@
 
 dojo.require("dojo.widget.*");
 dojo.require("dojo.widget.HtmlWidget");
-dojo.require("dojo.widget.Container");
-dojo.require("dojo.dom");
-dojo.require("dojo.html");
-dojo.require("dojo.style");
-dojo.require("dojo.event");
-
-dojo.widget.tags.addParseTreeHandler("dojo:FisheyeList");
-dojo.widget.tags.addParseTreeHandler("dojo:FisheyeListItem");
-
-dojo.widget.html.FisheyeList = function(){
-	dojo.widget.html.Container.call(this);
-}
-dojo.inherits(dojo.widget.html.FisheyeList, dojo.widget.html.Container);
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.html.util");
+dojo.require("dojo.event.*");
+
+/*
+ * summary
+ *	Menu similar to the fish eye menu on the Mac OS
+ * usage
+ *	<div dojoType="FisheyeList"
+ *	itemWidth="40" itemHeight="40"
+ *	itemMaxWidth="150" itemMaxHeight="150"
+ *	orientation="horizontal"
+ *	effectUnits="2"
+ *	itemPadding="10"
+ *	attachEdge="center"
+ *	labelEdge="bottom">
+ *
+ *		<div dojoType="FisheyeListItem"
+ *			id="item1"
+ *			onclick="alert('click on' + this.caption + '(from widget id ' + this.widgetId + ')!');"
+ *			caption="Item 1"
+ *			iconsrc="images/fisheye_1.png">
+ *		</div>
+ *		...
+ *	</div>
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.FisheyeList",
+	dojo.widget.HtmlWidget,
+function(){
+	this.pos = {x: -1, y: -1};		// current cursor position, relative to the grid
 
-dojo.lang.extend(dojo.widget.html.FisheyeList, {
-
-	templateString: '<div class="dojoHtmlFisheyeListBar"></div>',
-	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlFisheyeList.css"),
-	widgetType: "FisheyeList",
-
-	EDGE: {
+	this.EDGE = {
 		CENTER: 0,
 		LEFT: 1,
 		RIGHT: 2,
 		TOP: 3,
 		BOTTOM: 4
-	},
-
-	snarfChildDomOutput: true,
-	
-	pos: {x: -1, y: -1},		// current cursor position, relative to the grid
+	};
 	
 	// for conservative trigger mode, when triggered, timerScale is gradually increased from 0 to 1
-	timerScale: 1.0,
+	this.timerScale = 1.0;
 
-	/////////////////////////////////////////////////////////////////
-	//
-	// i spy OPTIONS!!!!
-	//
+},
+{
+	templateString: '<div class="dojoHtmlFisheyeListBar"></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/FisheyeList.css"),
+
+	isContainer: true,
+	snarfChildDomOutput: true,
 
+	// Integer
+	//	width of menu item (in pixels) in it's dormant state (when the mouse is far away)
 	itemWidth: 40,
+
+	// Integer
+	//	height of menu item (in pixels) in it's dormant state (when the mouse is far away)
 	itemHeight: 40,
 
+	// Integer
+	//	width of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
 	itemMaxWidth: 150,
+
+	// Integer
+	//	height of menu item (in pixels) in it's fully enlarged state (when the mouse is directly over it)
 	itemMaxHeight: 150,
 
+
+	// String
+	//	orientation of the menu, either "horizontal" or "vertical"
 	orientation: 'horizontal',
-	
-	conservativeTrigger: false,		// don't active menu until mouse is over an image (macintosh style)
 
+	// Boolean
+	//	if true, don't start enlarging menu items until mouse is over an image;
+	//	if false, start enlarging menu items as the mouse moves near them.
+	conservativeTrigger: false,
+
+	// Number
+	//	controls how much reaction the menu makes, relative to the distance of the mouse from the menu
 	effectUnits: 2,
+	
+	// Integer
+	//	padding (in pixels) betweeen each menu item
 	itemPadding: 10,
 
+	// String
+	//	controls the border that the menu items don't expand past;
+	//	for example, if set to "top", then the menu items will drop downwards as they expand.
+	// values
+	//	"center", "left", "right", "top", "bottom".
 	attachEdge: 'center',
+
+	// String
+	//	controls were the labels show up in relation to the menu item icons
+	// values
+	//	"center", "left", "right", "top", "bottom".
 	labelEdge: 'bottom',
 
+	// Boolean
+	//	for browsers that support svg, use the svg image (specified in FisheyeListIem.svgSrc)
+	//	rather than the iconSrc image attribute
 	enableCrappySvgSupport: false,
 
-
-	//
-	//
-	//
-	/////////////////////////////////////////////////////////////////
-
-	fillInTemplate: function(args, frag) {
-		//dojo.debug(this.orientation);
-
+	fillInTemplate: function() {
 		dojo.html.disableSelection(this.domNode);
 
-		this.isHorizontal = (this.orientation == 'horizontal') ? 1 : 0;
+		this.isHorizontal = (this.orientation == 'horizontal');
 		this.selectedNode = -1;
 
 		this.isOver = false;
@@ -101,9 +137,8 @@
 		//
 		// only some edges make sense...
 		//
-
-		this.anchorEdge = this.toEdge(this.attachEdge, this.EDGE.CENTER);
-		this.labelEdge  = this.toEdge(this.labelEdge,  this.EDGE.TOP);
+		this.anchorEdge = this._toEdge(this.attachEdge, this.EDGE.CENTER);
+		this.labelEdge  = this._toEdge(this.labelEdge,  this.EDGE.TOP);
 
 		if ( this.isHorizontal && (this.anchorEdge == this.EDGE.LEFT  )){ this.anchorEdge = this.EDGE.CENTER; }
 		if ( this.isHorizontal && (this.anchorEdge == this.EDGE.RIGHT )){ this.anchorEdge = this.EDGE.CENTER; }
@@ -116,11 +151,9 @@
 		if (!this.isHorizontal && (this.labelEdge == this.EDGE.TOP   )){ this.labelEdge = this.EDGE.LEFT; }
 		if (!this.isHorizontal && (this.labelEdge == this.EDGE.BOTTOM)){ this.labelEdge = this.EDGE.LEFT; }
 
-
 		//
 		// figure out the proximity size
 		//
-
 		this.proximityLeft   = this.itemWidth  * (this.effectUnits - 0.5);
 		this.proximityRight  = this.itemWidth  * (this.effectUnits - 0.5);
 		this.proximityTop    = this.itemHeight * (this.effectUnits - 0.5);
@@ -146,8 +179,22 @@
 		}
 	},
 	
-	postCreate: function(args, frag) {
+	postCreate: function() {
+		this._initializePositioning();
+
+		//
+		// in liberal trigger mode, activate menu whenever mouse is close
+		//
+		if( !this.conservativeTrigger ){
+			dojo.event.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
+		}
+		
+		// Deactivate the menu if mouse is moved off screen (doesn't work for FF?)
+		dojo.event.connect(document.documentElement, "onmouseout", this, "_onBodyOut");
+		dojo.event.connect(this, "addChild", this, "_initializePositioning");
+	},
 
+	_initializePositioning: function(){
 		this.itemCount = this.children.length;
 
 		this.barWidth  = (this.isHorizontal ? this.itemCount : 1) * this.itemWidth;
@@ -159,7 +206,6 @@
 		//
 		// calculate effect ranges for each item
 		//
-
 		for (var i=0; i<this.children.length; i++){
 
 			this.children[i].posX = this.itemWidth  * (this.isHorizontal ? i : 0);
@@ -187,15 +233,12 @@
 			//dojo.debug('effect range for '+i+' is '+range_lhs+'/'+range_rhs);
 		}
 
-
 		//
 		// create the bar
 		//
-
 		this.domNode.style.width = this.barWidth + 'px';
 		this.domNode.style.height = this.barHeight + 'px';
 
-
 		//
 		// position the items
 		//
@@ -227,88 +270,78 @@
 		//
 		// calc the grid
 		//
-
-		this.calcHitGrid();
-
-		//
-		// in liberal trigger mode, activate menu whenever mouse is close
-		//
-		if( !this.conservativeTrigger ){
-			dojo.event.connect(document.documentElement, "onmousemove", this, "mouseHandler");
-		}
-		
-		// Deactivate the menu if mouse is moved off screen (doesn't work for FF?)
-		dojo.event.connect(document.documentElement, "onmouseout", this, "onBodyOut");
+		this._calcHitGrid();
 	},
 
-	onBodyOut: function(e){
+	_onBodyOut: function(/*Event*/ e){
 		// clicking over an object inside of body causes this event to fire; ignore that case
-		if( dojo.html.overElement(dojo.html.body(), e) ){
+		if( dojo.html.overElement(dojo.body(), e) ){
 			return;
 		}
-		this.setDormant(e);
+		this._setDormant(e);
 	},
 
-	// when mouse moves out of menu's range
-	setDormant: function(e){
+	_setDormant: function(/*Event*/ e){
+		// summary: called when mouse moves out of menu's range
+
 		if( !this.isOver ){ return; }	// already dormant?
 		this.isOver = false;
 
 		if ( this.conservativeTrigger ) {
 			// user can't re-trigger the menu expansion
 			// until he mouses over a icon again
-			dojo.event.disconnect(document.documentElement, "onmousemove", this, "mouseHandler");
+			dojo.event.disconnect(document.documentElement, "onmousemove", this, "_onMouseMove");
 		}
-		this.onGridMouseMove(-1, -1);
+		this._onGridMouseMove(-1, -1);
 	},
 
-	// when mouse is moved into menu's range
-	setActive: function(e){
+	_setActive: function(/*Event*/ e){
+		// summary: called when mouse is moved into menu's range
+
 		if( this.isOver ){ return; }	// already activated?
 		this.isOver = true;
 
 		if ( this.conservativeTrigger ) {
 			// switch event handlers so that we handle mouse events from anywhere near
 			// the menu
-			dojo.event.connect(document.documentElement, "onmousemove", this, "mouseHandler");
+			dojo.event.connect(document.documentElement, "onmousemove", this, "_onMouseMove");
 
 			this.timerScale=0.0;
 
 			// call mouse handler to do some initial necessary calculations/positioning
-			this.mouseHandler(e);
+			this._onMouseMove(e);
 
 			// slowly expand the icon size so it isn't jumpy
-			this.expandSlowly();
+			this._expandSlowly();
 		}
 	},
 
-	// when mouse is moved
-	mouseHandler: function(e) {
-		var p = this.getCursorPos(e);
-
-		if ((p.x >= this.hitX1) && (p.x <= this.hitX2) &&
-			(p.y >= this.hitY1) && (p.y <= this.hitY2)){
+	_onMouseMove: function(/*Event*/ e) {
+		// summary: called when mouse is moved
+		if ((e.pageX >= this.hitX1) && (e.pageX <= this.hitX2) &&
+			(e.pageY >= this.hitY1) && (e.pageY <= this.hitY2)){
 			if( !this.isOver ){
-				this.setActive(e);
+				this._setActive(e);
 			}
-			this.onGridMouseMove(p.x-this.hitX1, p.y-this.hitY1);
+			this._onGridMouseMove(e.pageX-this.hitX1, e.pageY-this.hitY1);
 		}else{
 			if (this.isOver){
-				this.setDormant(e);
+				this._setDormant(e);
 			}
 		}
 	},
 
 	onResized: function() {
-		this.calcHitGrid();
+		this._calcHitGrid();
 	},
 
-	onGridMouseMove: function(x, y){
+	_onGridMouseMove: function(x, y){
+		// summary: called when mouse is moved in the vicinity of the menu
 		this.pos = {x:x, y:y};
-		this.paint();
+		this._paint();
 	},
 	
-	paint: function(){
+	_paint: function(){
 		var x=this.pos.x;
 		var y=this.pos.y;
 
@@ -317,7 +350,6 @@
 		//
 		// figure out our main index
 		//
-
 		var pos = this.isHorizontal ? x : y;
 		var prx = this.isHorizontal ? this.proximityLeft : this.proximityTop;
 		var siz = this.isHorizontal ? this.itemWidth : this.itemHeight;
@@ -330,11 +362,9 @@
 
 		if (max_off_cen > this.effectUnits){ max_off_cen = this.effectUnits; }
 
-
 		//
 		// figure out our off-axis weighting
 		//
-
 		var off_weight = 0;
 
 		if (this.anchorEdge == this.EDGE.BOTTOM){
@@ -354,7 +384,6 @@
 			off_weight = (cen2 < 0.5) ? 1 : (this.totalWidth - x) / (this.proximityRight + (this.itemWidth / 2));
 		}
 		if (this.anchorEdge == this.EDGE.CENTER){
-
 			if (this.isHorizontal){
 				off_weight = y / (this.totalHeight);
 			}else{
@@ -368,18 +397,13 @@
 			off_weight *= 2;
 		}
 
-
 		//
 		// set the sizes
 		//
-
 		for(var i=0; i<this.itemCount; i++){
-
-			var weight = this.weightAt(cen, i);
-
+			var weight = this._weighAt(cen, i);
 			if (weight < 0){weight = 0;}
-
-			this.setitemsize(i, weight * off_weight);
+			this._setItemSize(i, weight * off_weight);
 		}
 
 		//
@@ -401,40 +425,16 @@
 			offset = (cen - main_p) * ((this.isHorizontal ? this.itemWidth : this.itemHeight) - this.children[main_p].sizeMain);
 		}
 
-		this.positionElementsFrom(main_p, offset);
+		this._positionElementsFrom(main_p, offset);
 	},
 
-	weightAt: function(cen, i){
-
+	_weighAt: function(/*Integer*/ cen, /*Integer*/ i){
 		var dist = Math.abs(cen - i);
-
 		var limit = ((cen - i) > 0) ? this.children[i].effectRangeRght : this.children[i].effectRangeLeft;
-
-		return (dist > limit) ? 0 : (1 - dist / limit);
-	},
-
-	positionFromNode: function(p, w){
-
-		//
-		// we need to grow all the nodes growing out from node 'i'
-		//
-
-		this.setitemsize(p, w);
-
-		var wx = w;
-		for(var i=p; i<this.itemCount; i++){
-			wx = 0.8 * wx;
-			this.setitemsize(i, wx);
-		}
-
-		var wx = w;
-		for(var i=p; i>=0; i--){
-			wx = 0.8 * wx;
-			this.setitemsize(i, wx);
-		}
+		return (dist > limit) ? 0 : (1 - dist / limit);			// Integer
 	},
 
-	setitemsize: function(p, scale){
+	_setItemSize: function(p, scale){
 		scale *= this.timerScale;
 		var w = Math.round(this.itemWidth  + ((this.itemMaxWidth  - this.itemWidth ) * scale));
 		var h = Math.round(this.itemHeight + ((this.itemMaxHeight - this.itemHeight) * scale));
@@ -448,24 +448,16 @@
 			this.children[p].sizeOff  = h;
 
 			var y = 0;
-
 			if (this.anchorEdge == this.EDGE.TOP){
-
 				y = (this.children[p].cenY - (this.itemHeight / 2));
-
 			}else if (this.anchorEdge == this.EDGE.BOTTOM){
-
 				y = (this.children[p].cenY - (h - (this.itemHeight / 2)));
-
 			}else{
-
 				y = (this.children[p].cenY - (h / 2));
 			}
 
 			this.children[p].usualX = Math.round(this.children[p].cenX - (w / 2));
-			
 			this.children[p].domNode.style.top  = y + 'px';
-
 			this.children[p].domNode.style.left  = this.children[p].usualX + 'px';
 
 		}else{
@@ -477,16 +469,11 @@
 			this.children[p].sizeMain = h;
 
 			var x = 0;
-
 			if (this.anchorEdge == this.EDGE.LEFT){
-
 				x = this.children[p].cenX - (this.itemWidth / 2);
-
 			}else if (this.anchorEdge == this.EDGE.RIGHT){
-
 				x = this.children[p].cenX - (w - (this.itemWidth / 2));
 			}else{
-
 				x = this.children[p].cenX - (w / 2);
 			}
 
@@ -504,7 +491,7 @@
 		}
 	},
 
-	positionElementsFrom: function(p, offset){
+	_positionElementsFrom: function(p, offset){
 
 		var pos = 0;
 
@@ -515,17 +502,14 @@
 			pos = Math.round(this.children[p].usualY + offset);
 			this.children[p].domNode.style.top = pos + 'px';
 		}
-		this.positionLabel(this.children[p]);
+		this._positionLabel(this.children[p]);
 
 
 		//
 		// position before
 		//
-
 		var bpos = pos;
-
 		for(var i=p-1; i>=0; i--){
-
 			bpos -= this.children[i].sizeMain;
 
 			if (this.isHorizontal){
@@ -533,17 +517,14 @@
 			}else{
 				this.children[i].domNode.style.top = bpos + 'px';
 			}
-			this.positionLabel(this.children[i]);
+			this._positionLabel(this.children[i]);
 		}
 
 		//
 		// position after
 		//
-
 		var apos = pos;
-
 		for(var i=p+1; i<this.itemCount; i++){
-
 			apos += this.children[i-1].sizeMain;
 
 			if (this.isHorizontal){
@@ -551,53 +532,45 @@
 			}else{
 				this.children[i].domNode.style.top = apos + 'px';
 			}
-			this.positionLabel(this.children[i]);
+			this._positionLabel(this.children[i]);
 		}
 
 	},
 
-	positionLabel: function(itm){
+	_positionLabel: function(itm){
 
 		var x = 0;
 		var y = 0;
 		
-		var labelW = dojo.style.getOuterWidth(itm.lblNode);
-		var labelH = dojo.style.getOuterHeight(itm.lblNode);
+		var mb = dojo.html.getMarginBox(itm.lblNode);
 
 		if (this.labelEdge == this.EDGE.TOP){
-			x = Math.round((itm.sizeW / 2) - (labelW / 2));
-			y = -labelH;
+			x = Math.round((itm.sizeW / 2) - (mb.width / 2));
+			y = -mb.height;
 		}
 
 		if (this.labelEdge == this.EDGE.BOTTOM){
-			x = Math.round((itm.sizeW / 2) - (labelW / 2));
+			x = Math.round((itm.sizeW / 2) - (mb.width / 2));
 			y = itm.sizeH;
 		}
 
 		if (this.labelEdge == this.EDGE.LEFT){
-			x = -labelW;
-			y = Math.round((itm.sizeH / 2) - (labelH / 2));
+			x = -mb.width;
+			y = Math.round((itm.sizeH / 2) - (mb.height / 2));
 		}
 
 		if (this.labelEdge == this.EDGE.RIGHT){
 			x = itm.sizeW;
-			y = Math.round((itm.sizeH / 2) - (labelH / 2));
+			y = Math.round((itm.sizeH / 2) - (mb.height / 2));
 		}
 
 		itm.lblNode.style.left = x + 'px';
 		itm.lblNode.style.top  = y + 'px';
 	},
 
-	getCursorPos: function(e){
-		return {
-			'x': e.pageX || e.clientX + dojo.html.body().scrollLeft,
-			'y': e.pageY || e.clientY + dojo.html.body().scrollTop
-			};
-	},
-
-	calcHitGrid: function(){
+	_calcHitGrid: function(){
 
-		var pos = dojo.style.getAbsolutePosition(this.domNode);
+		var pos = dojo.html.getAbsolutePosition(this.domNode, true);
 
 		this.hitX1 = pos.x - this.proximityLeft;
 		this.hitY1 = pos.y - this.proximityTop;
@@ -607,36 +580,54 @@
 		//dojo.debug(this.hitX1+','+this.hitY1+' // '+this.hitX2+','+this.hitY2);
 	},
 
-	toEdge: function(inp, def){
+	_toEdge: function(inp, def){
 		return this.EDGE[inp.toUpperCase()] || def;
 	},
 	
-	// slowly expand the image to user specified max size
-	expandSlowly: function(){
+	_expandSlowly: function(){
+		// summary: slowly expand the image to user specified max size
 		if( !this.isOver ){ return; }
 		this.timerScale += 0.2;
-		this.paint();
+		this._paint();
 		if ( this.timerScale<1.0 ) {
-			dojo.lang.setTimeout(this, "expandSlowly", 10);
+			dojo.lang.setTimeout(this, "_expandSlowly", 10);
 		}
-	}
+	},
 
+	destroy: function(){
+		// need to disconnect when we destroy
+		dojo.event.disconnect(document.documentElement, "onmouseout", this, "_onBodyOut");
+		dojo.event.disconnect(document.documentElement, "onmousemove", this, "_onMouseMove");
+		dojo.widget.FisheyeList.superclass.destroy.call(this);
+	}
 });
 
-dojo.widget.html.FisheyeListItem = function(){
-	dojo.widget.HtmlWidget.call(this);
-}
-dojo.inherits(dojo.widget.html.FisheyeListItem, dojo.widget.HtmlWidget);
-
-dojo.lang.extend(dojo.widget.html.FisheyeListItem, {
-	widgetType: "FisheyeListItem",
-	
-	// Constructor arguments
+/*
+ * summary
+ *	Menu item inside of a FisheyeList.
+ *	See FisheyeList documentation for details on usage.
+ */
+dojo.widget.defineWidget(
+	"dojo.widget.FisheyeListItem",
+	dojo.widget.HtmlWidget,
+{
+	// String
+	//	pathname to image file (jpg, gif, png, etc.) of icon for this menu item
 	iconSrc: "",
+
+	// String
+	//	pathname to svg file of icon for this menu item
 	svgSrc: "",
+	
+	// String
+	//	label to print next to the icon, when it is moused-over
 	caption: "",
 
-	blankImgPath: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
+	// String
+	//	will be set to the id of the orginal div element
+	id: "",
+
+	_blankImgPath: dojo.uri.dojoUri("src/widget/templates/images/blank.gif"),
 
 	templateString:
 		'<div class="dojoHtmlFisheyeListItem">' +
@@ -644,8 +635,6 @@
 		'  <div class="dojoHtmlFisheyeListItemLabel" dojoAttachPoint="lblNode"></div>' +
 		'</div>',
 	
-	imgNode: null,
-
 	fillInTemplate: function() {
 		//
 		// set image
@@ -653,13 +642,22 @@
 		// this.parent.enableCrappySvgSupport is not available to this function
 		//
 		if (this.svgSrc != ""){
-			this.svgNode = this.createSvgNode(this.svgSrc);
+			this.svgNode = this._createSvgNode(this.svgSrc);
 			this.domNode.appendChild(this.svgNode);
 			this.imgNode.style.display = 'none';
-		} else if((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4)==".png")&&(dojo.render.html.ie)){
+		} else if((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4)==".png")&&(dojo.render.html.ie)&&(!dojo.render.html.ie70)){
+			/* we set the id of the new fisheyeListItem to the id of the div defined in the HTML */
+			if (dojo.dom.hasParent(this.imgNode) && this.id != ""){
+				var parent = this.imgNode.parentNode;
+				parent.setAttribute("id", this.id);
+			}
 			this.imgNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='scale')";
-			this.imgNode.src = this.blankImgPath.toString();
+			this.imgNode.src = this._blankImgPath.toString();
 		} else {
+			if (dojo.dom.hasParent(this.imgNode) && this.id != ""){
+				var parent = this.imgNode.parentNode;
+				parent.setAttribute("id", this.id);
+			}
 			this.imgNode.src = this.iconSrc;
 		}
 
@@ -672,8 +670,7 @@
 		dojo.html.disableSelection(this.domNode);
 	},
 	
-	createSvgNode: function(src){
-
+	_createSvgNode: function(src){
 		var elm = document.createElement('embed');
 		elm.src = src;
 		elm.type = 'image/svg+xml';
@@ -721,22 +718,25 @@
 		return elm;
 	},
 
-	onMouseOver: function(e) {
+	onMouseOver: function(/*Event*/ e) {
+		// summary: callback when user moves mouse over this menu item
 		// in conservative mode, don't activate the menu until user mouses over an icon
 		if( !this.parent.isOver ){
-			this.parent.setActive(e);
+			this.parent._setActive(e);
 		}
 		if ( this.caption != "" ) {
 			dojo.html.addClass(this.lblNode, "selected");
-			this.parent.positionLabel(this);
+			this.parent._positionLabel(this);
 		}
 	},
 	
-	onMouseOut: function() {
+	onMouseOut: function(/*Event*/ e) {
+		// summary: callback when user moves mouse off of this menu item
 		dojo.html.removeClass(this.lblNode, "selected");
 	},
 
-	onClick: function() {
+	onClick: function(/*Event*/ e) {
+		// summary: user overridable callback when user clicks this menu item
 	}
 });
 

Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FloatingPane.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FloatingPane.js?view=diff&rev=474551&r1=474550&r2=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FloatingPane.js (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/FloatingPane.js Mon Nov 13 14:54:45 2006
@@ -1,5 +1,5 @@
 /*
-	Copyright (c) 2004-2005, The Dojo Foundation
+	Copyright (c) 2004-2006, The Dojo Foundation
 	All Rights Reserved.
 
 	Licensed under the Academic Free License version 2.1 or above OR the
@@ -8,521 +8,434 @@
 		http://dojotoolkit.org/community/licensing.shtml
 */
 
-dojo.provide("dojo.widget.FloatingPane");
-dojo.provide("dojo.widget.html.FloatingPane");
-
-//
-// this widget provides a window-like floating pane
-//
-
-dojo.require("dojo.widget.*");
-dojo.require("dojo.widget.Manager");
-dojo.require("dojo.html");
-dojo.require("dojo.style");
-dojo.require("dojo.dom");
-dojo.require("dojo.widget.ContentPane");
-dojo.require("dojo.widget.LayoutPane");
-dojo.require("dojo.dnd.HtmlDragMove");
-dojo.require("dojo.dnd.HtmlDragMoveSource");
-dojo.require("dojo.dnd.HtmlDragMoveObject");
-
-dojo.widget.html.FloatingPane = function(){
-	dojo.widget.html.LayoutPane.call(this);
-}
-
-dojo.inherits(dojo.widget.html.FloatingPane, dojo.widget.html.LayoutPane);
-
-dojo.lang.extend(dojo.widget.html.FloatingPane, {
-	widgetType: "FloatingPane",
-
-	// Constructor arguments
-	title: '',
-	iconSrc: '',
-	hasShadow: false,
-	constrainToContainer: false,
-	taskBarId: "",
-	resizable: true,	// note: if specified, user must include ResizeHandle
-	overflow: "",
-
-	resizable: false,
-	titleBarDisplay: "fancy",
-	titleHeight: 22,	// workaround to CSS loading race condition bug
-
-	href: "",
-	extractContent: true,
-	parseContent: true,
-	cacheContent: true,
-
-	// FloatingPane supports 3 modes for the client area (the part below the title bar)
-	//  default - client area  is a ContentPane, that can hold
-	//      either inlined data and/or data downloaded from a URL
-	//  layout - the client area is a layout pane
-	//  none - the user specifies a single widget which becomes the content pane
-	contentWrapper: "default",
-
-	containerNode: null,
-	domNode: null,
-	clientPane: null,
-	dragBar: null,
-
-	windowState: "normal",
-	displayCloseAction: false,
-
-	maxTaskBarConnectAttempts: 5,
-	taskBarConnectAttempts: 0,
-
-	minimizeIcon: dojo.uri.dojoUri("src/widget/templates/images/floatingPaneMinimize.gif"),
-	maximizeIcon: dojo.uri.dojoUri("src/widget/templates/images/floatingPaneMaximize.gif"),
-	restoreIcon: dojo.uri.dojoUri("src/widget/templates/images/floatingPaneRestore.gif"),
-	closeIcon: dojo.uri.dojoUri("src/widget/templates/images/floatingPaneClose.gif"),
-	titleBarBackground: dojo.uri.dojoUri("src/widget/templates/images/titlebar-bg.jpg"),
-
-	shadowPng: dojo.uri.dojoUri("src/widget/templates/images/shadow"),
-	shadowThickness: 8,
-	shadowOffset: 15,
-
-	templateString: '<div></div>',
-	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlFloatingPane.css"),
-
-	initialized: false,
-
-	addChild: function(child, overrideContainerNode, pos, ref, insertIndex) {
-		this.clientPane.addChild(child, overrideContainerNode, pos, ref, insertIndex);
-	},
-
-	// make a widget container to hold all the contents of the floating pane (other than the
-	// title and the resize bar at the bottom)
-	_makeClientPane: function(clientDiv){
-		var args = {
-			layoutAlign: "client", 
-			id:this.widgetId+"_client",
-			href: this.href, 
-			cacheContent: this.cacheContent, 
-			extractContent: this.extractContent,
-			parseContent: this.parseContent
-		};
-		var pane = this._createPane(this.contentWrapper=="layout"?"LayoutPane":"ContentPane", clientDiv, args);
-		return pane;
-	},
-
-	fillInTemplate: function(args, frag){
-		var source = this.getFragNodeRef(frag);
-
-		// Copy style info and id from input node to output node
-		this.domNode.style.cssText = source.style.cssText;
-		dojo.html.addClass(this.domNode, dojo.html.getClass(source));
-		dojo.html.addClass(this.domNode, "dojoFloatingPane");
-		this.domNode.style.position="absolute";
-		this.domNode.id = source.id;
-		if(dojo.render.html.safari){
-			dojo.html.body().appendChild(this.domNode);
-		}
-
-		// make client pane wrapper to hold the contents of this floating pane
-		if(this.contentWrapper!="none"){
-			var clientDiv = document.createElement('div');
-			dojo.dom.moveChildren(source, clientDiv, 0);
-			this.clientPane = this._makeClientPane(clientDiv);
-		}
-		
-		if (this.titleBarDisplay != "none") {
-			// this is our chrome
-			var chromeDiv = document.createElement('div');
-			dojo.html.addClass(chromeDiv, 'dojoFloatingPaneDragbar');
-			chromeDiv.style.height=this.titleHeight+"px";	// workaround CSS loading race condition bug
-			
-			this.dragBar = this._createPane("LayoutPane", chromeDiv, {layoutAlign: 'top', id:this.widgetId+"_chrome"});
-			dojo.html.disableSelection(this.dragBar.domNode);
-
-			if( this.titleBarDisplay == "fancy"){
-				// image background to get gradient
-				var img = document.createElement('img');
-				img.src = this.titleBarBackground;
-				dojo.html.addClass(img, 'dojoFloatingPaneDragbarBackground');
-				var backgroundPane = dojo.widget.createWidget("ContentPane", {layoutAlign:"flood", id:this.widgetId+"_titleBackground"}, img);
-				this.dragBar.addChild(backgroundPane);
-			}
-
-			//Title Bar
-			var titleBar = document.createElement('div');
-			dojo.html.addClass(titleBar, "dojoFloatingPaneTitleBar");
-			dojo.html.disableSelection(titleBar);
-
-			//TitleBarActions
-			var titleBarActions = document.createElement('div');
-			dojo.html.addClass(titleBarActions, "dojoFloatingPaneActions");
-
-			//Title Icon
-			if(this.iconSrc!=""){
-				var titleIcon = document.createElement('img');
-				dojo.html.addClass(titleIcon,"dojoTitleBarIcon");
-				titleIcon.src = this.iconSrc;						
-				titleBar.appendChild(titleIcon);
-			}
-
-			//Title text  
-			var titleText = document.createTextNode(this.title)
-			titleBar.appendChild(titleText);
-
-			if (this.resizable) {
-
-				//FloatingPane Action Minimize
-				this.minimizeAction = document.createElement("img");
-				dojo.html.addClass(this.minimizeAction, "dojoFloatingPaneActionItem");
-				this.minimizeAction.src = this.minimizeIcon;	
-				titleBarActions.appendChild(this.minimizeAction);
-				dojo.event.connect(this.minimizeAction, 'onclick', this, 'minimizeWindow');
-
-				//FloatingPane Action Restore
-				this.restoreAction = document.createElement("img");
-				dojo.html.addClass(this.restoreAction, "dojoFloatingPaneActionItem");
-				this.restoreAction.src = this.restoreIcon;	
-				titleBarActions.appendChild(this.restoreAction);
-				dojo.event.connect(this.restoreAction, 'onclick', this, 'restoreWindow');
-
-				if (this.windowState != "normal") {
-					this.restoreAction.style.display="inline";
-				} else {
-					this.restoreAction.style.display="none";
-				}
-
-				//FloatingPane Action Maximize
-				this.maximizeAction = document.createElement("img");
-				dojo.html.addClass(this.maximizeAction, "dojoFloatingPaneActionItem");
-				this.maximizeAction.src = this.maximizeIcon;	
-				titleBarActions.appendChild(this.maximizeAction);
-				dojo.event.connect(this.maximizeAction, 'onclick', this, 'maximizeWindow');
-
-				if (this.windowState != "maximized") {
-					this.maximizeAction.style.display="inline";	
-				} else {
-					this.maximizeAction.style.display="none";	
-				}	
-
-			}
-
-			if (this.displayCloseAction) {
-				//FloatingPane Action Close
-				var closeAction= document.createElement("img");
-				dojo.html.addClass(closeAction, "dojoFloatingPaneActionItem");
-				closeAction.src = this.closeIcon;	
-				titleBarActions.appendChild(closeAction);
-				dojo.event.connect(closeAction, 'onclick', this, 'closeWindow');
-			}
-
-
-			chromeDiv.appendChild(titleBar);
-			chromeDiv.appendChild(titleBarActions);
-		}
-
-		if ( this.resizable ) {
-			// add the resize handle
-			var resizeDiv = document.createElement('div');
-			dojo.html.addClass(resizeDiv, "dojoFloatingPaneResizebar");
-			dojo.html.disableSelection(resizeDiv);
-			var rh = dojo.widget.createWidget("ResizeHandle", {targetElmId: this.widgetId, id:this.widgetId+"_resize"});
-			this.resizePane = this._createPane("ContentPane", resizeDiv, {layoutAlign: "bottom"});
-			this.resizePane.addChild(rh);
-		}
-
-		// add a drop shadow
-		this._makeShadow();
-
-		dojo.event.connect(this.domNode, 'onmousedown', this, 'onMouseDown');
-
-		// Prevent IE bleed-through problem
-		this.bgIframe = new dojo.html.BackgroundIframe();
-		if( this.bgIframe.iframe ){
-			this.domNode.appendChild(this.bgIframe.iframe);
-		}
-		if ( this.isVisible() ) {
-			this.bgIframe.show();
-		};
-
-		if( this.taskBarId ){
-			this.taskBarSetup();
-		}
-
-		if (dojo.hostenv.post_load_) {
-			this.setInitialWindowState();
-		} else {
-			dojo.addOnLoad(this, "setInitialWindowState");
-		}
-		if(dojo.render.html.safari){
-			dojo.html.body().removeChild(this.domNode);
-		}
-
-		dojo.widget.html.FloatingPane.superclass.postCreate.call(this, args, frag);
-	},
-
-	postCreate: function(args, frag){
-		// Make the client pane.  It will either be the widget specified by the user,
-		// or a wrapper widget
-		if(this.contentWrapper=="none"){
-			// the user has specified a single widget which will become our content
-			this.clientPane = this.children[0];
-			this.domNode.appendChild(this.clientPane.domNode);
-		}else{
-			// move our 'children' into the client pane
-			// we already moved the domnodes, but now we need to move the 'children'
-			var kids = this.children.concat();
-			this.children = [];
-	
-			for(var i=0; i<kids.length; i++){
-				if (kids[i].ownerPane == this){
-					this.children.push(kids[i]);
-				}else{
-					if(this.contentWrapper=="layout"){
-						this.clientPane.addChild(kids[i]);
-					}else{
-						this.clientPane.children.push(kids[i]);
-					}
-				}
-			}
-		}
-		dojo.html.addClass(this.clientPane.domNode, 'dojoFloatingPaneClient');
-		this.clientPane.layoutAlign="client";
-		this.clientPane.ownerPane=this;
-		if (this.overflow != "") {
-			this.clientPane.domNode.style.overflow=this.overflow;
-		}
-
-		if (this.titleBarDisplay != "none") {
-			var drag = new dojo.dnd.HtmlDragMoveSource(this.domNode);
-	
-			if (this.constrainToContainer) {
-				drag.constrainTo();
-			}
-	
-			drag.setDragHandle(this.dragBar.domNode);
-		}
-
-		this.initialized=true;
-	},
-
-	_makeShadow: function(){
-		if ( this.hasShadow ) {
-			// make all the pieces of the shadow, and position/size them as much
-			// as possible (but a lot of the coordinates are set in sizeShadow
-			this.shadow={};
-			var x1 = -1 * this.shadowThickness;
-			var y0 = this.shadowOffset;
-			var y1 = this.shadowOffset + this.shadowThickness;
-			this._makeShadowPiece("ul", "top", y0, "left", x1);
-			this._makeShadowPiece("l", "top", y1, "left", x1, "scale");
-			this._makeShadowPiece("ur", "top", y0, "left", 0);
-			this._makeShadowPiece("r", "top", y1, "left", 0, "scale");
-			this._makeShadowPiece("bl", "top", 0, "left", x1);
-			this._makeShadowPiece("b", "top", 0, "left", 0, "crop");
-			this._makeShadowPiece("br", "top", 0, "left", 0);
-		}
-	},
-
-	_makeShadowPiece: function(name, vertAttach, vertCoord, horzAttach, horzCoord, sizing){
-		var img;
-		var url = this.shadowPng + name.toUpperCase() + ".png";
-		if(dojo.render.html.ie){
-			img=document.createElement("div");
-			img.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+url+"'"+
-			(sizing?", sizingMethod='"+sizing+"'":"") + ")";
-		}else{
-			img=document.createElement("img");
-			img.src=url;
-		}
-		img.style.position="absolute";
-		img.style[vertAttach]=vertCoord+"px";
-		img.style[horzAttach]=horzCoord+"px";
-		img.style.width=this.shadowThickness+"px";
-		img.style.height=this.shadowThickness+"px";
-		this.shadow[name]=img;
-		this.domNode.appendChild(img);
-	},
-
-	_sizeShadow: function(width, height){
-		if ( this.shadow ) {
-			var sideHeight = height - (this.shadowOffset+this.shadowThickness+1);
-			this.shadow.l.style.height = sideHeight+"px";
-			this.shadow.r.style.height = sideHeight+"px";
-			this.shadow.b.style.width = (width-1)+"px";
-			this.shadow.bl.style.top = (height-1)+"px";
-			this.shadow.b.style.top = (height-1)+"px";
-			this.shadow.br.style.top = (height-1)+"px";
-			this.shadow.ur.style.left = (width-1)+"px";
-			this.shadow.r.style.left = (width-1)+"px";
-			this.shadow.br.style.left = (width-1)+"px";
-		}
-	},
-
-	maximizeWindow: function(evt) {
-		this.previousWidth= this.domNode.style.width;
-		this.previousHeight= this.domNode.style.height;
-		this.previousLeft = this.domNode.style.left;
-		this.previousTop = this.domNode.style.top;
-
-		this.domNode.style.left =
-			dojo.style.getPixelValue(this.domNode.parentNode, "padding-left", true) + "px";
-		this.domNode.style.top =
-			dojo.style.getPixelValue(this.domNode.parentNode, "padding-top", true) + "px";
-
-		if ((this.domNode.parentNode.nodeName.toLowerCase() == 'body')) {
-			dojo.style.setOuterWidth(this.domNode, dojo.html.getViewportWidth()-dojo.style.getPaddingWidth(dojo.html.body()));
-			dojo.style.setOuterHeight(this.domNode, dojo.html.getViewportHeight()-dojo.style.getPaddingHeight(dojo.html.body()));
-		} else {
-			dojo.style.setOuterWidth(this.domNode, dojo.style.getContentWidth(this.domNode.parentNode));
-			dojo.style.setOuterHeight(this.domNode, dojo.style.getContentHeight(this.domNode.parentNode));
-		}	
-		this.maximizeAction.style.display="none";	
-		this.restoreAction.style.display="inline";	
-		this.windowState="maximized";
-		this.onResized();
-	},
-
-	minimizeWindow: function(evt) {
-		this.hide();
-		if (this.resizable) {
-			this.maximizeAction.style.display="inline";	
-			this.restoreAction.style.display="inline";	
-		}
-
-		this.windowState = "minimized";
-	},
-
-	restoreWindow: function(evt) {
-		if (this.previousWidth && this.previousHeight && this.previousLeft && this.previousTop) {
-			this.domNode.style.width = this.previousWidth;
-			this.domNode.style.height = this.previousHeight;
-			this.domNode.style.left = this.previousLeft;
-			this.domNode.style.top = this.previousTop;
-			dojo.widget.html.FloatingPane.superclass.onResized.call(this);
-		}
-
-		if (this.widgetState != "maximized") {
-			this.show();
-		}
-
-		if (this.resizable) {
-			this.maximizeAction.style.display="inline";	
-			this.restoreAction.style.display="none";	
-		}
-
-		this.bringToTop();
-		this.windowState="normal";
-	},
-
-	closeWindow: function(evt) {
-		this.destroy();
-	},
-
-	onMouseDown: function(evt) {
-		this.bringToTop();
-	},
-
-	bringToTop: function() {
-		var floatingPaneStartingZ = 100;
-		var floatingPanes= dojo.widget.manager.getWidgetsByType("FloatingPane");
-		var windows = []
-		var y=0;
-		for (var x=0; x<floatingPanes.length; x++) {
-			if (this.widgetId != floatingPanes[x].widgetId) {
-					windows.push(floatingPanes[x]);
-			}
-		}
-
-		windows.sort(function(a,b) {
-			return a.domNode.style.zIndex - b.domNode.style.zIndex;
-		});
-		
-		windows.push(this);
-
-		for (x=0; x<windows.length;x++) {
-			windows[x].domNode.style.zIndex = floatingPaneStartingZ + x;
-		}
-	},
-
-	setInitialWindowState: function() {
-		if (this.windowState == "maximized") {
-			this.maximizeWindow();
-			this.show();
-			this.bringToTop();
-			return;
-		}
-
-		if (this.windowState=="normal") {
-			dojo.lang.setTimeout(this, this.onResized, 50);
-			this.show();
-			this.bringToTop();
-			return;
-		}
-
-		if (this.windowState=="minimized") {
-			this.hide();
-			return;
-		}
-
-		this.windowState="minimized";
-	},
-
-	// add icon to task bar, connected to me
-	taskBarSetup: function() {
-		var taskbar = dojo.widget.getWidgetById(this.taskBarId);
-		if (!taskbar){
-			if (this.taskBarConnectAttempts <  this.maxTaskBarConnectAttempts) {
-				dojo.lang.setTimeout(this, this.taskBarSetup, 50);
-				this.taskBarConnectAttempts++;
-			} else {
-				dojo.debug("Unable to connect to the taskBar");
-			}
-			return;
-		}
-		taskbar.addChild(this);
-	},
-
-	onResized: function(){
-		if( !this.isVisible() ){ return; }
-		var newHeight = dojo.style.getInnerHeight(this.domNode);
-		var newWidth = dojo.style.getInnerWidth(this.domNode);
-	
-		//if ( newWidth != this.width || newHeight != this.height ) {
-			this.width = newWidth;
-			this.height = newHeight;
-			this._sizeShadow(newWidth, newHeight);
-			dojo.widget.html.FloatingPane.superclass.onResized.call(this);
-		//}
-
-		// bgIframe is a child of this.domNode, so position should be relative to [0,0]
-		if(this.bgIframe){
-			this.bgIframe.size([0, 0, newWidth, newHeight]);
-		}
-	},
-
-	hide: function(){
-		dojo.widget.html.FloatingPane.superclass.hide.call(this);
-		if(this.bgIframe){
-			this.bgIframe.hide();
-		}
-	},
-
-	show: function(){
-		dojo.widget.html.FloatingPane.superclass.show.call(this);
-		if(this.bgIframe){
-			this.bgIframe.show();
-		}
-	},
-
-	_createPane: function(type, node, args){
-		var pane = dojo.widget.createWidget(type, args, node);
-		dojo.widget.html.FloatingPane.superclass.addChild.call(this,pane);
-		pane.ownerPane=this;
-		return pane;
-	},
-	
-	setUrl: function(url){
-		this.clientPane.setUrl(url);
-	},
-	
-	setContent: function(str){
-		this.clientPane.setContent(str);
-	}
-});
-
-dojo.widget.tags.addParseTreeHandler("dojo:FloatingPane");
+dojo.provide("dojo.widget.FloatingPane");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.html.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.lfx.shadow");
+dojo.require("dojo.widget.html.layout");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.dnd.HtmlDragMove");
+dojo.require("dojo.widget.Dialog");		// for ModalFloatingPane
+dojo.require("dojo.widget.ResizeHandle");
+
+// summary
+//	Base class for FloatingPane, ModalFloatingPane
+dojo.declare(
+	"dojo.widget.FloatingPaneBase",
+	null,
+	{
+		// String
+		//	text to display in floating pane's title bar (ex: "My Window")
+		title: '',
+		
+		// String
+		//	path of icon to display in floating pane's title bar
+		iconSrc: '',
+		
+		// Boolean
+		//	if true, display a shadow behind the floating pane
+		hasShadow: false,
+		
+		// Boolean
+		//	if true, and the floating pane is inside another container (ContentPane, another FloatingPane, etc.),
+		//	then don't allow the floating pane to be dragged outside of it's container
+		constrainToContainer: false,
+		
+		// String
+		//	widget id of TaskBar widget;
+		//	if specified, then an icon for this FloatingPane will be added to the specified TaskBar
+		taskBarId: "",
+		
+		// Boolean
+		//	if true, allow user to resize floating pane
+		resizable: true,
+		
+		// Boolean
+		//	if true, display title bar for this floating pane
+		titleBarDisplay: true,
+
+		// String
+		//	control whether window is initially not displayed ("minimized"), displayed full screen ("maximized"),
+		//	or just displayed normally ("normal")
+		// Values
+		//	"normal", "maximized", "minimized"
+		windowState: "normal",
+		
+		// Boolean
+		//	display button to close window
+		displayCloseAction: false,
+		
+		// Boolean
+		//	display button to minimize window (ie, window disappears so only the taskbar item remains)
+		displayMinimizeAction: false,
+
+		// Boolean
+		//	display button to maximize window (ie, to take up the full screen)
+		displayMaximizeAction: false,
+
+		// Related to connecting to taskbar
+		// TODO: use topics rather than repeated connect attempts?
+		_max_taskBarConnectAttempts: 5,
+		_taskBarConnectAttempts: 0,
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/FloatingPane.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/FloatingPane.css"),
+
+		fillInFloatingPaneTemplate: function(args, frag){
+			// summary: this should be called by fillInTemplate() of the widget that I'm mixed into
+
+			// Copy style info from input node to output node
+			var source = this.getFragNodeRef(frag);
+			dojo.html.copyStyle(this.domNode, source);
+	
+			// necessary for safari, khtml (for computing width/height)
+			dojo.body().appendChild(this.domNode);
+	
+			// if display:none then state=minimized, otherwise state=normal
+			if(!this.isShowing()){
+				this.windowState="minimized";
+			}
+	
+			// <img src=""> can hang IE!  better get rid of it
+			if(this.iconSrc==""){
+				dojo.html.removeNode(this.titleBarIcon);
+			}else{
+				this.titleBarIcon.src = this.iconSrc.toString();// dojo.uri.Uri obj req. toString()
+			}
+	
+			if(this.titleBarDisplay){	
+				this.titleBar.style.display="";
+				dojo.html.disableSelection(this.titleBar);
+	
+				this.titleBarIcon.style.display = (this.iconSrc=="" ? "none" : "");
+	
+				this.minimizeAction.style.display = (this.displayMinimizeAction ? "" : "none");
+				this.maximizeAction.style.display= 
+					(this.displayMaximizeAction && this.windowState!="maximized" ? "" : "none");
+				this.restoreAction.style.display= 
+					(this.displayMaximizeAction && this.windowState=="maximized" ? "" : "none");
+				this.closeAction.style.display= (this.displayCloseAction ? "" : "none");
+	
+				this.drag = new dojo.dnd.HtmlDragMoveSource(this.domNode);	
+				if (this.constrainToContainer) {
+					this.drag.constrainTo();
+				}
+				this.drag.setDragHandle(this.titleBar);
+	
+				var self = this;
+	
+				dojo.event.topic.subscribe("dragMove",
+					function (info){
+						if (info.source.domNode == self.domNode){
+							dojo.event.topic.publish('floatingPaneMove', { source: self } );
+						}
+					}
+				);
+			}
+	
+			if(this.resizable){
+				this.resizeBar.style.display="";
+				this.resizeHandle = dojo.widget.createWidget("ResizeHandle", {targetElmId: this.widgetId, id:this.widgetId+"_resize"});
+				this.resizeBar.appendChild(this.resizeHandle.domNode);
+			}
+	
+			// add a drop shadow
+			if(this.hasShadow){
+				this.shadow=new dojo.lfx.shadow(this.domNode);
+			}
+	
+			// Prevent IE bleed-through problem
+			this.bgIframe = new dojo.html.BackgroundIframe(this.domNode);
+	
+			if( this.taskBarId ){
+				this._taskBarSetup();
+			}
+	
+			// counteract body.appendChild above
+			dojo.body().removeChild(this.domNode);
+		},
+	
+		postCreate: function(){
+			if (dojo.hostenv.post_load_) {
+				this._setInitialWindowState();
+			} else {
+				dojo.addOnLoad(this, "_setInitialWindowState");
+			}
+		},
+	
+		maximizeWindow: function(/*Event*/ evt) {
+			// summary: maximize the window
+			var mb = dojo.html.getMarginBox(this.domNode);
+			this.previous={
+				width: mb.width || this.width,
+				height: mb.height || this.height,
+				left: this.domNode.style.left,
+				top: this.domNode.style.top,
+				bottom: this.domNode.style.bottom,
+				right: this.domNode.style.right
+			};
+			if(this.domNode.parentNode.style.overflow.toLowerCase() != 'hidden'){
+				this.parentPrevious={
+					overflow: this.domNode.parentNode.style.overflow
+				};
+				dojo.debug(this.domNode.parentNode.style.overflow);
+				this.domNode.parentNode.style.overflow = 'hidden';
+			}
+
+			this.domNode.style.left =
+				dojo.html.getPixelValue(this.domNode.parentNode, "padding-left", true) + "px";
+			this.domNode.style.top =
+				dojo.html.getPixelValue(this.domNode.parentNode, "padding-top", true) + "px";
+
+			if ((this.domNode.parentNode.nodeName.toLowerCase() == 'body')) {
+				var viewport = dojo.html.getViewport();
+				var padding = dojo.html.getPadding(dojo.body());
+				this.resizeTo(viewport.width-padding.width, viewport.height-padding.height);
+			} else {
+				var content = dojo.html.getContentBox(this.domNode.parentNode);
+				this.resizeTo(content.width, content.height);
+			}
+			this.maximizeAction.style.display="none";
+			this.restoreAction.style.display="";
+
+			//disable resize and drag
+			if(this.resizeHandle){
+				this.resizeHandle.domNode.style.display="none";
+			}
+			this.drag.setDragHandle(null);
+
+			this.windowState="maximized";
+		},
+	
+		minimizeWindow: function(/*Event*/ evt) {
+			// summary: hide the window so that only the icon in the taskbar is shown
+			this.hide();
+			for(var attr in this.parentPrevious){
+				this.domNode.parentNode.style[attr] = this.parentPrevious[attr];
+			}
+			this.lastWindowState = this.windowState;
+			this.windowState = "minimized";
+		},
+	
+		restoreWindow: function(/*Event*/ evt) {
+			// summary: set the winow to normal size (neither maximized nor minimized)
+			if (this.windowState=="minimized") {
+				this.show();
+				if(this.lastWindowState == "maximized"){
+					this.domNode.parentNode.style.overflow = 'hidden';
+					this.windowState="maximized";
+				}else{ //normal
+					this.windowState="normal";
+				}
+			} else if (this.windowState=="maximized"){
+				for(var attr in this.previous){
+					this.domNode.style[attr] = this.previous[attr];
+				}
+				for(var attr in this.parentPrevious){
+					this.domNode.parentNode.style[attr] = this.parentPrevious[attr];
+				}
+				this.resizeTo(this.previous.width, this.previous.height);
+				this.previous=null;
+				this.parentPrevious=null;
+
+				this.restoreAction.style.display="none";
+				this.maximizeAction.style.display=this.displayMaximizeAction ? "" : "none";
+
+				if(this.resizeHandle){
+					this.resizeHandle.domNode.style.display="";
+				}
+				this.drag.setDragHandle(this.titleBar);
+				this.windowState="normal";
+			} else { //normal
+				// do nothing
+			}
+		},
+
+		toggleDisplay: function(){
+			// summary: switch between hidden mode and displayed mode (either maximized or normal, depending on state before window was minimized)
+			if(this.windowState=="minimized"){
+				this.restoreWindow();
+			}else{
+				this.minimizeWindow();
+			}
+		},
+
+		closeWindow: function(/*Event*/ evt) {
+			// summary: destroy this window
+			dojo.html.removeNode(this.domNode);
+			this.destroy();
+		},
+	
+		onMouseDown: function(/*Event*/ evt) {
+			// summary: callback when user clicks anywhere on the floating pane
+			this.bringToTop();
+		},
+	
+		bringToTop: function() {
+			// summary
+			//	all the floating panes are stacked in z-index order; bring this floating pane to the top of that stack,
+			//	so that it's displayed in front of all the other floating panes
+			var floatingPanes= dojo.widget.manager.getWidgetsByType(this.widgetType);
+			var windows = [];
+			for (var x=0; x<floatingPanes.length; x++) {
+				if (this.widgetId != floatingPanes[x].widgetId) {
+						windows.push(floatingPanes[x]);
+				}
+			}
+	
+			windows.sort(function(a,b) {
+				return a.domNode.style.zIndex - b.domNode.style.zIndex;
+			});
+			
+			windows.push(this);
+	
+			var floatingPaneStartingZ = 100;
+			for (x=0; x<windows.length;x++) {
+				windows[x].domNode.style.zIndex = floatingPaneStartingZ + x*2;
+			}
+		},
+	
+		_setInitialWindowState: function() {
+			if(this.isShowing()){
+				this.width=-1;	// force resize
+				var mb = dojo.html.getMarginBox(this.domNode);
+				this.resizeTo(mb.width, mb.height);
+			}
+			if (this.windowState == "maximized") {
+				this.maximizeWindow();
+				this.show();
+				return;
+			}
+	
+			if (this.windowState=="normal") {
+				this.show();
+				return;
+			}
+	
+			if (this.windowState=="minimized") {
+				this.hide();
+				return;
+			}
+	
+			this.windowState="minimized";
+		},
+	
+		_taskBarSetup: function() {
+			// summary: add icon to task bar, connected to me
+			var taskbar = dojo.widget.getWidgetById(this.taskBarId);
+			if (!taskbar){
+				if (this._taskBarConnectAttempts <  this._max_taskBarConnectAttempts) {
+					dojo.lang.setTimeout(this, this._taskBarSetup, 50);
+					this._taskBarConnectAttempts++;
+				} else {
+					dojo.debug("Unable to connect to the taskBar");
+				}
+				return;
+			}
+			taskbar.addChild(this);
+		},
+
+		showFloatingPane: function(){
+			// summary:
+			//	bring this floating pane to the top
+			this.bringToTop();
+		},
+
+		onFloatingPaneShow: function(){
+			// summary: callback for when someone calls FloatingPane.show
+			var mb = dojo.html.getMarginBox(this.domNode);
+			this.resizeTo(mb.width, mb.height);
+		},
+	
+		// summary: set the floating pane to the given size
+		resizeTo: function(/*Integer*/ width, /*Integer*/ height){
+			dojo.html.setMarginBox(this.domNode, { width: width, height: height });
+	
+			dojo.widget.html.layout(this.domNode,
+				[
+				  {domNode: this.titleBar, layoutAlign: "top"},
+				  {domNode: this.resizeBar, layoutAlign: "bottom"},
+				  {domNode: this.containerNode, layoutAlign: "client"}
+				] );
+	
+			// If any of the children have layoutAlign specified, obey it
+			dojo.widget.html.layout(this.containerNode, this.children, "top-bottom");
+			
+			this.bgIframe.onResized();
+			if(this.shadow){ this.shadow.size(width, height); }
+			this.onResized();
+		},
+	
+		checkSize: function() {
+			// summary
+			//	checkSize() 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...
+			// TODO: unless we are maximized.  then we should resize ourself.
+		}
+	}
+);
+
+// summary
+//	A non-modal floating window.
+//	Attaches to a Taskbar which has an icon for each window.
+//	Must specify size (like style="width: 500px; height: 500px;"),
+dojo.widget.defineWidget(
+	"dojo.widget.FloatingPane",
+	[dojo.widget.ContentPane, dojo.widget.FloatingPaneBase], 
+{
+	fillInTemplate: function(args, frag){	
+		this.fillInFloatingPaneTemplate(args, frag);
+		dojo.widget.FloatingPane.superclass.fillInTemplate.call(this, args, frag);
+	},
+	postCreate: function(){
+		dojo.widget.FloatingPaneBase.prototype.postCreate.apply(this, arguments);
+		dojo.widget.FloatingPane.superclass.postCreate.apply(this, arguments);
+	},
+	show: function(){
+		dojo.widget.FloatingPane.superclass.show.apply(this, arguments);
+		this.showFloatingPane();
+	},
+	onShow: function(){
+		dojo.widget.FloatingPane.superclass.onShow.call(this);
+		this.onFloatingPaneShow();
+	}
+});
+
+
+// summary
+//	A modal floating window.
+//	This widget is similar to the Dialog widget, but the window, unlike the Dialog, can be moved.
+//	Must specify size (like style="width: 500px; height: 500px;"),
+dojo.widget.defineWidget(
+	"dojo.widget.ModalFloatingPane",
+	[dojo.widget.FloatingPane, dojo.widget.ModalDialogBase],
+	{
+		windowState: "minimized",
+		displayCloseAction: true,
+		postCreate: function(){
+			dojo.widget.ModalDialogBase.prototype.postCreate.call(this);
+			dojo.widget.ModalFloatingPane.superclass.postCreate.call(this);
+		},
+		show: function(){
+			dojo.widget.ModalFloatingPane.superclass.show.apply(this, arguments);
+			this.showModalDialog();
+			this.placeModalDialog();
+			//place the background div under this modal pane
+			this.shared.bg.style.zIndex = this.domNode.style.zIndex-1;
+		},
+		hide: function(){
+			this.hideModalDialog();
+			dojo.widget.ModalFloatingPane.superclass.hide.apply(this, arguments);
+		},
+		closeWindow: function(){
+			this.hide();
+			dojo.widget.ModalFloatingPane.superclass.closeWindow.apply(this, arguments);
+		}
+	}
+);