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/09/23 01:22:51 UTC

svn commit: r449122 [26/40] - in /tapestry/tapestry4/trunk/tapestry-framework/src: java/org/apache/tapestry/ java/org/apache/tapestry/dojo/ java/org/apache/tapestry/dojo/form/ java/org/apache/tapestry/dojo/html/ java/org/apache/tapestry/form/ java/org/...

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Editor2Toolbar.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Editor2Toolbar.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Editor2Toolbar.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Editor2Toolbar.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,564 @@
+/*
+	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.Editor2Toolbar");
+
+dojo.require("dojo.lang.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.display");
+dojo.require("dojo.widget.RichText");
+dojo.require("dojo.widget.PopupContainer");
+dojo.require("dojo.widget.ColorPalette");
+
+dojo.widget.Editor2ToolbarItemManager = {
+	_registeredItemHandlers: [],
+	registerHandler: function(obj, func){
+		if(arguments.length == 1){
+//			for(i in this._registeredItemHandlers){
+//				if(func === this._registeredItemHandlers[i]){
+//					dojo.debug("Editor2ToolbarItemManager handler "+func+" is already registered, ignored");
+//					return;
+//				}
+//			}
+			this._registeredItemHandlers.push(obj);
+		}else{
+			this._registeredItemHandlers.push(function(){return obj[func].apply(obj, arguments);});
+		}
+	},
+	removeHandler: function(func){
+		for(var i in this._registeredItemHandlers){
+			if(func === this._registeredItemHandlers[i]){
+				delete this._registeredItemHandlers[i];
+				return;
+			}
+		}
+		dojo.debug("Editor2ToolbarItemManager handler "+func+" is not registered, can not remove.");
+	},
+	destroy: function(){
+		for(var i in this._registeredItemHandlers){
+			delete this._registeredItemHandlers[i];
+		}
+	},
+	getToolbarItem: function(name){
+		var item;
+		name = name.toLowerCase();
+		for(var i in this._registeredItemHandlers){
+			item = this._registeredItemHandlers[i](name);
+			if(item){
+				break;
+			}
+		}
+
+		if(!item){
+			switch(name){
+				//button for builtin functions
+				case 'bold':
+				case 'copy':
+				case 'cut':
+				case 'delete':
+				case 'indent':
+				case 'inserthorizontalrule':
+				case 'insertorderedlist':
+				case 'insertunorderedlist':
+				case 'italic':
+				case 'justifycenter':
+				case 'justifyfull':
+				case 'justifyleft': 
+				case 'justifyright':
+				case 'outdent':
+				case 'paste':
+				case 'redo':
+				case 'removeformat':
+				case 'selectall':
+				case 'strikethrough':
+				case 'subscript':
+				case 'superscript':
+				case 'underline':
+				case 'undo': 
+				case 'unlink':
+				case 'createlink':
+				case 'insertimage':
+				//extra simple buttons
+				case 'htmltoggle':
+					item = new dojo.widget.Editor2ToolbarButton(name);
+					break;
+				case 'forecolor': 
+				case 'hilitecolor':
+					item = new dojo.widget.Editor2ToolbarColorPaletteButton(name);
+					break;
+				case 'plainformatblock':
+					item = new dojo.widget.Editor2ToolbarFormatBlockPlainSelect("formatblock");
+					break;
+				case 'formatblock':
+					item = new dojo.widget.Editor2ToolbarFormatBlockSelect("formatblock");
+					break;
+				case 'fontsize':
+					item = new dojo.widget.Editor2ToolbarFontSizeSelect("fontsize");
+					break;
+				case 'fontname':
+					item = new dojo.widget.Editor2ToolbarFontNameSelect("fontname");
+					break;
+				case 'inserttable':
+				case 'insertcell':
+				case 'insertcol':
+				case 'insertrow':
+				case 'deletecells':
+				case 'deletecols':
+				case 'deleterows':
+				case 'mergecells':
+				case 'splitcell':
+					dojo.debug(name + " is implemented in dojo.widget.Editor2Plugin.TableOperation, please require it first.");
+					break;
+				//TODO:
+				case 'inserthtml':
+				case 'blockdirltr':
+				case 'blockdirrtl':
+				case 'dirltr':
+				case 'dirrtl':
+				case 'inlinedirltr':
+				case 'inlinedirrtl':
+					dojo.debug("Not yet implemented toolbar item: "+name);
+					break;
+				default:
+					dojo.debug("dojo.widget.Editor2ToolbarItemManager.getToolbarItem: Unknown toolbar item: "+name);
+			}
+		}
+		return item;
+	}
+};
+
+dojo.addOnUnload(dojo.widget.Editor2ToolbarItemManager, "destroy");
+
+dojo.declare("dojo.widget.Editor2ToolbarButton", null,{
+	initializer: function(name){
+		this._name = name;
+		this._command = dojo.widget.Editor2Manager.getCommand(name);
+	},
+	create: function(node, toolbar, isMenu){
+		this._domNode = node;
+		//make this unselectable: different browsers
+		//use different properties for this, so use
+		//js do it automatically
+		this.disableSelection(this._domNode);
+		this._parentToolbar = toolbar;
+		dojo.event.connect(this._domNode, 'onclick', this, 'onClick');
+		if(!isMenu){
+			dojo.event.connect(this._domNode, 'onmouseover', this, 'onMouseOver');
+			dojo.event.connect(this._domNode, 'onmouseout', this, 'onMouseOut');
+		}
+	},
+	disableSelection: function(rootnode){
+		dojo.html.disableSelection(rootnode);
+		var nodes = rootnode.all || rootnode.getElementsByTagName("*");
+		for(var x=0; x<nodes.length; x++){
+			dojo.html.disableSelection(nodes[x]);
+		}
+	},
+	onMouseOver: function(){
+		if(this._command.getState() != dojo.widget.Editor2Manager.commandState.Disabled){
+			this.highlightToolbarItem();
+		}
+	},
+	onMouseOut: function(){
+		this.unhighlightToolbarItem();
+	},
+	destroy: function(){
+		this._domNode = null;
+		delete this._command;
+		this._parentToolbar = null;
+	},
+	onClick: function(e){
+		if(this._domNode && !this._domNode.disabled && this._command){
+			e.preventDefault();
+			e.stopPropagation();
+			this._command.execute();
+		}
+	},
+	refreshState: function(){
+		if(this._domNode && this._command){
+			var em = dojo.widget.Editor2Manager;
+			var state = this._command.getState();
+			if(state != this._lastState){
+				switch(state){
+					case em.commandState.Latched:
+						this.latchToolbarItem();
+						break;
+					case em.commandState.Enabled:
+						this.enableToolbarItem();
+						break;
+					case em.commandState.Disabled:
+					default:
+						this.disableToolbarItem();
+				}
+				this._lastState = state;
+			}
+			return state;
+		}
+	},
+
+	latchToolbarItem: function(){
+		this._domNode.disabled = false;
+		this.removeToolbarItemStyle(this._domNode);
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarLatchedItemStyle);
+	},
+
+	enableToolbarItem: function(){
+		this._domNode.disabled = false;
+		this.removeToolbarItemStyle(this._domNode);
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarEnabledItemStyle);
+	},
+
+	disableToolbarItem: function(){
+		this._domNode.disabled = true;
+		this.removeToolbarItemStyle(this._domNode);
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarDisabledItemStyle);
+	},
+
+	highlightToolbarItem: function(){
+		dojo.html.addClass(this._domNode, this._parentToolbar.ToolbarHighlightedItemStyle);
+	},
+
+	unhighlightToolbarItem: function(){
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarHighlightedItemStyle);
+	},
+
+	removeToolbarItemStyle: function(){
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarEnabledItemStyle);
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarLatchedItemStyle);
+		dojo.html.removeClass(this._domNode, this._parentToolbar.ToolbarDisabledItemStyle);
+		this.unhighlightToolbarItem();
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarDropDownButton", dojo.widget.Editor2ToolbarButton,{
+	onClick: function(){
+		if(this._domNode){
+			if(!this._dropdown){
+				this._dropdown = dojo.widget.createWidget("PopupContainer", {});
+				this._domNode.appendChild(this._dropdown.domNode);
+			}
+			if(this._dropdown.isShowingNow){
+				this._dropdown.close();
+			}else{
+				this.onDropDownShown();	
+				this._dropdown.open(this._domNode, null, this._domNode);
+			}
+		}
+	},
+	destroy: function(){
+		this.onDropDownDestroy();
+		if(this._dropdown){
+			this._dropdown.destroy();
+		}
+		dojo.widget.Editor2ToolbarDropDownButton.superclass.destroy.call(this);
+	},
+	onDropDownShown: function(){},
+	onDropDownDestroy: function(){}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarColorPaletteButton", dojo.widget.Editor2ToolbarDropDownButton,{
+	onDropDownShown: function(){
+		if(!this._colorpalette){
+			this._colorpalette = dojo.widget.createWidget("ColorPalette", {});
+			this._dropdown.addChild(this._colorpalette);
+
+			this.disableSelection(this._dropdown.domNode);
+			this.disableSelection(this._colorpalette.domNode);
+			//do we need a destory to delete this._colorpalette manually?
+			//I assume as it is added to this._dropdown via addChild, it
+			//should be deleted when this._dropdown is destroyed
+
+			dojo.event.connect(this._colorpalette, "onColorSelect", this, 'setColor');
+			dojo.event.connect(this._dropdown, "open", this, 'latchToolbarItem');
+			dojo.event.connect(this._dropdown, "close", this, 'enableToolbarItem');
+		}
+	},
+	setColor: function(color){
+		this._dropdown.close();
+		this._command.execute(color);
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFormatBlockPlainSelect", dojo.widget.Editor2ToolbarButton,{
+	create: function(node, toolbar){
+		//TODO: check node is a select
+		this._domNode = node;
+		this.disableSelection(this._domNode);
+		this._parentToolbar = toolbar;
+		dojo.event.connect(this._domNode, 'onchange', this, 'onChange');
+	},
+
+	destroy: function(){
+		this._domNode = null;
+		this._command = null;
+		this._parentToolbar = null;
+	},
+
+	onChange: function(){
+		if(this._domNode){
+			var sv = this._domNode.value.toLowerCase();
+			this._command.execute(sv);
+		}
+	},
+
+	refreshState: function(){
+		if(this._domNode && this._command){
+			dojo.widget.Editor2ToolbarFormatBlockPlainSelect.superclass.refreshState.call(this);
+			var format = this._command.getValue();
+			if(!format){ format = ""; }
+			dojo.lang.forEach(this._domNode.options, function(item){
+				if(item.value.toLowerCase() == format.toLowerCase()){
+					item.selected = true;
+				}
+			});
+		}
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarComboItem", dojo.widget.Editor2ToolbarDropDownButton,{
+	href: null,
+	create: function(node, toolbar){
+		dojo.widget.Editor2ToolbarComboItem.superclass.create.call(this, node, toolbar);
+		//do not use lazy initilization, as we need the local names in refreshState()
+		if(!this._contentPane){
+			dojo.require("dojo.widget.ContentPane");
+			this._contentPane = dojo.widget.createWidget("ContentPane", {preload: 'true'});
+			this._contentPane.addOnLoad(this, "setup");
+			this._contentPane.setUrl(this.href);
+		}
+	},
+
+	onMouseOver: function(e){
+		dojo.html.addClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectStyle);
+	},
+	onMouseOut:function(e){
+		dojo.html.removeClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectStyle);
+	},
+
+	onDropDownShown: function(){
+		if(!this._dropdown.__addedContentPage){
+			this._dropdown.addChild(this._contentPane);
+			this._dropdown.__addedContentPage = true;
+		}
+	},
+
+	//overload this to connect event
+	setup: function(){},
+
+	onChange: function(e){
+		var name = e.currentTarget.getAttribute("dropDownItemName");
+		this._command.execute(name);
+		this._dropdown.close();
+	},
+
+	onMouseOverItem: function(e){
+		dojo.html.addClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectItemStyle);
+	},
+
+	onMouseOutItem: function(e){
+		dojo.html.removeClass(e.currentTarget, this._parentToolbar.ToolbarHighlightedSelectItemStyle);
+	},
+
+	//overload this to update GUI item
+	refreshState: function(){}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFormatBlockSelect", dojo.widget.Editor2ToolbarComboItem,{
+	href: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbar_FormatBlock.html"),
+
+	setup: function(){
+		dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.setup.call(this);
+
+		var nodes = this._contentPane.domNode.all || this._contentPane.domNode.getElementsByTagName("*");
+		this._blockNames = {};
+		this._blockDisplayNames = {};
+		for(var x=0; x<nodes.length; x++){
+			var node = nodes[x];
+			dojo.html.disableSelection(node);
+			var name=node.getAttribute("dropDownItemName")
+			if(name){
+				this._blockNames[name] = node;
+				var childrennodes = node.getElementsByTagName(name);
+				this._blockDisplayNames[name] = childrennodes[childrennodes.length-1].innerHTML;
+			}
+		}
+		for(var name in this._blockNames){
+			dojo.event.connect(this._blockNames[name], "onclick", this, "onChange");
+			dojo.event.connect(this._blockNames[name], "onmouseover", this, "onMouseOverItem");
+			dojo.event.connect(this._blockNames[name], "onmouseout", this, "onMouseOutItem");
+		}
+	},
+
+	onDropDownDestroy: function(){
+		if(this._blockNames){
+			for(var name in this._blockNames){
+				delete this._blockNames[name];
+				delete this._blockDisplayNames[name];
+			}
+		}
+	},
+
+	refreshState: function(){
+		if(this._command){
+			//dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.refreshState.call(this);
+			var format = this._command.getValue();
+			if(format == this._lastSelectedFormat && this._blockDisplayNames){
+				return;
+			}
+			this._lastSelectedFormat = format;
+			var label = this._domNode.getElementsByTagName("label")[0];
+			var isSet = false;
+			if(this._blockDisplayNames){
+				for(var name in this._blockDisplayNames){
+					if(name == format){
+						label.innerHTML = 	this._blockDisplayNames[name];
+						isSet = true;
+						break;
+					}
+				}
+				if(!isSet){
+					label.innerHTML = "&nbsp;";
+				}
+			}
+		}
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFontSizeSelect", dojo.widget.Editor2ToolbarComboItem,{
+	href: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbar_FontSize.html"),
+
+	setup: function(){
+		dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.setup.call(this);
+
+		var nodes = this._contentPane.domNode.all || this._contentPane.domNode.getElementsByTagName("*");
+		this._fontsizes = {};
+		this._fontSizeDisplayNames = {};
+		for(var x=0; x<nodes.length; x++){
+			var node = nodes[x];
+			dojo.html.disableSelection(node);
+			var name=node.getAttribute("dropDownItemName")
+			if(name){
+				this._fontsizes[name] = node;
+				this._fontSizeDisplayNames[name] = node.getElementsByTagName('font')[0].innerHTML;
+			}
+		}
+		for(var name in this._fontsizes){
+			dojo.event.connect(this._fontsizes[name], "onclick", this, "onChange");
+			dojo.event.connect(this._fontsizes[name], "onmouseover", this, "onMouseOverItem");
+			dojo.event.connect(this._fontsizes[name], "onmouseout", this, "onMouseOutItem");
+		}
+	},
+
+	onDropDownDestroy: function(){
+		if(this._fontsizes){
+			for(var name in this._fontsizes){
+				delete this._fontsizes[name];
+				delete this._fontSizeDisplayNames[name];
+			}
+		}
+	},
+
+	refreshState: function(){
+		if(this._command){
+			//dojo.widget.Editor2ToolbarFormatBlockSelect.superclass.refreshState.call(this);
+			var size = this._command.getValue();
+			if(size == this._lastSelectedSize && this._fontSizeDisplayNames){
+				return;
+			}
+			this._lastSelectedSize = size;
+			var label = this._domNode.getElementsByTagName("label")[0];
+			var isSet = false;
+			if(this._fontSizeDisplayNames){
+				for(var name in this._fontSizeDisplayNames){
+					if(name == size){
+						label.innerHTML = 	this._fontSizeDisplayNames[name];
+						isSet = true;
+						break;
+					}
+				}
+				if(!isSet){
+					label.innerHTML = "&nbsp;";
+				}
+			}
+		}
+	}
+});
+
+dojo.declare("dojo.widget.Editor2ToolbarFontNameSelect", dojo.widget.Editor2ToolbarFontSizeSelect,{
+	href: dojo.uri.dojoUri("src/widget/templates/Editor2/EditorToolbar_FontName.html")
+});
+
+dojo.widget.defineWidget(
+	"dojo.widget.Editor2Toolbar",
+	dojo.widget.HtmlWidget,
+	{
+		templatePath: dojo.uri.dojoUri("src/widget/templates/EditorToolbar.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/EditorToolbar.css"),
+
+		// DOM Nodes
+		saveButton: null,
+
+		ToolbarLatchedItemStyle: "ToolbarButtonLatched",
+		ToolbarEnabledItemStyle: "ToolbarButtonEnabled",
+		ToolbarDisabledItemStyle: "ToolbarButtonDisabled",
+		ToolbarHighlightedItemStyle: "ToolbarButtonHighlighted",
+		ToolbarHighlightedSelectStyle: "ToolbarSelectHighlighted",
+		ToolbarHighlightedSelectItemStyle: "ToolbarSelectHighlightedItem",
+
+//		itemNodeType: 'span', //all the items (with attribute dojoETItemName set) defined in the toolbar should be a of this type
+
+		postCreate: function(){
+			var nodes = dojo.html.getElementsByClass("dojoEditorToolbarItem", this.domNode/*, this.itemNodeType*/);
+
+			this.items = {};
+			for(var x=0; x<nodes.length; x++){
+				var node = nodes[x];
+				var itemname = node.getAttribute("dojoETItemName");
+				if(itemname){
+					var item = dojo.widget.Editor2ToolbarItemManager.getToolbarItem(itemname);
+					if(item){
+						item.create(node, this);
+						this.items[itemname.toLowerCase()] = item;
+					}else{
+						//hide unsupported toolbar items
+						node.style.display = "none";
+					}
+				}
+			}
+		},
+
+		update: function(){
+			for(var cmd in this.items){
+				this.items[cmd].refreshState();
+			}
+		},
+
+		destroy: function(){
+			for(var it in this.items){
+				this.items[it].destroy();
+				delete this.items[it];
+			}
+			dojo.widget.Editor2Toolbar.superclass.destroy.call(this);
+		}//,
+
+		// stub for observers
+//		exec: function(what, arg){ /* dojo.debug(what, new Date()); */ }
+	},
+	"html",
+	function(){
+		dojo.event.connect(this, "fillInTemplate", dojo.lang.hitch(this, function(){
+			if(dojo.render.html.ie){
+				this.domNode.style.zoom = 1.0;
+			}
+		}));
+	}
+);

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/Editor2Toolbar.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FilteringTable.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FilteringTable.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FilteringTable.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FilteringTable.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,897 @@
+/*
+	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(){
+		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(s){
+		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;
+	},
+
+	//	custom data access.
+	getByRow: function(/*HTMLTableRow*/row){
+		return this.store.getByKey(dojo.html.getAttribute(row, "value")); 
+	},
+	getDataByRow: function(/*HTMLTableRow*/row){
+		return this.store.getDataByKey(dojo.html.getAttribute(row, "value"));
+	},
+
+	getRow: function(/* Object */ obj){
+		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];
+			}
+		}
+		return null;
+	},
+	getColumnIndex: function(/* string */fieldPath){
+		for(var i=0; i<this.columns.length; i++){
+			if(this.columns[i].getField() == fieldPath){
+				return i;
+			}
+		}
+		return -1;
+	},
+
+	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){
+		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){
+		var v = this.store.getByKey(val);
+		if(v){
+			return v.isSelected;
+		}
+		return false;
+	},
+	isIndexSelected: function(/* number */idx){
+		var v = this.store.getByIndex(idx);
+		if(v){
+			return v.isSelected;
+		}
+		return false;
+	},
+	isRowSelected: function(/* HTMLTableRow */row){
+		var v = this.getByRow(row);
+		if(v){
+			return v.isSelected;
+		}
+		return false;
+	},
+
+	reset: function(){
+		this.store.clearData();
+		this.columns = [];
+		this.sortInformation = [ {index:0, direction:0} ];
+		this.resetSelections();
+		this.isInitialized = false;
+		this.onReset();
+	},
+	resetSelections: function(){
+		this.store.forEach(function(element){
+			element.isSelected = false;
+		});
+	},
+	onReset:function(){ },
+
+	//	selection and toggle functions
+	select: function(/*object*/ obj){
+		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){
+		this.select(this.store.getDataByKey(val));
+	},
+	selectByIndex: function(/*number*/ idx){
+		this.select(this.store.getDataByIndex(idx));
+	},
+	selectByRow: function(/*HTMLTableRow*/ row){
+		this.select(this.getDataByRow(row));
+	},
+	selectAll: function(){
+		this.store.forEach(function(element){
+			element.isSelected = true;
+		});
+	},
+	onDataSelect: function(/* object */obj){ },
+
+	toggleSelection: function(/*object*/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){
+		this.toggleSelection(this.store.getDataByKey(val));
+	},
+	toggleSelectionByIndex: function(/*number*/idx){
+		this.toggleSelection(this.store.getDataByIndex(idx));
+	},
+	toggleSelectionByRow: function(/*HTMLTableRow*/row){
+		this.toggleSelection(this.getDataByRow(row));
+	},
+	toggleAll: function(){
+		this.store.forEach(function(element){
+			element.isSelected = !element.isSelected;
+		});
+	},
+	onDataToggle: function(/* object */obj){ },
+
+	//	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){
+		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){
+		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;
+	},
+	fillCell: function(/* HTMLTableCell */cell, /* object */meta, /* object */val){
+		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(){
+	/*	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(){
+		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(){ 
+		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);
+			}
+		});
+	},
+//	fillInTemplate: function(args, frag){ },
+	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: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FilteringTable.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FisheyeList.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FisheyeList.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FisheyeList.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FisheyeList.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,741 @@
+/*
+	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.FisheyeList");
+dojo.provide("dojo.widget.FisheyeListItem");
+
+//
+// TODO
+// fix SVG support, and turn it on only if the browser supports it
+// fix really long labels in vertical mode
+//
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.html.style");
+dojo.require("dojo.html.selection");
+dojo.require("dojo.html.util");
+dojo.require("dojo.event.*");
+
+dojo.widget.defineWidget(
+	"dojo.widget.FisheyeList",
+	dojo.widget.HtmlWidget,
+{
+	templateString: '<div class="dojoHtmlFisheyeListBar"></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/FisheyeList.css"),
+
+	EDGE: {
+		CENTER: 0,
+		LEFT: 1,
+		RIGHT: 2,
+		TOP: 3,
+		BOTTOM: 4
+	},
+
+	isContainer: true,
+	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,
+
+	/////////////////////////////////////////////////////////////////
+	//
+	// i spy OPTIONS!!!!
+	//
+
+	itemWidth: 40,
+	itemHeight: 40,
+
+	itemMaxWidth: 150,
+	itemMaxHeight: 150,
+
+	orientation: 'horizontal',
+	
+	conservativeTrigger: false,		// don't active menu until mouse is over an image (macintosh style)
+
+	effectUnits: 2,
+	itemPadding: 10,
+
+	attachEdge: 'center',
+	labelEdge: 'bottom',
+
+	enableCrappySvgSupport: false,
+
+
+	//
+	//
+	//
+	/////////////////////////////////////////////////////////////////
+
+	fillInTemplate: function(args, frag) {
+		//dojo.debug(this.orientation);
+
+		dojo.html.disableSelection(this.domNode);
+
+		this.isHorizontal = (this.orientation == 'horizontal') ? 1 : 0;
+		this.selectedNode = -1;
+
+		this.isOver = false;
+		this.hitX1 = -1;
+		this.hitY1 = -1;
+		this.hitX2 = -1;
+		this.hitY2 = -1;
+
+		//
+		// only some edges make sense...
+		//
+
+		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; }
+		if (!this.isHorizontal && (this.anchorEdge == this.EDGE.TOP   )){ this.anchorEdge = this.EDGE.CENTER; }
+		if (!this.isHorizontal && (this.anchorEdge == this.EDGE.BOTTOM)){ this.anchorEdge = this.EDGE.CENTER; }
+
+		if (this.labelEdge == this.EDGE.CENTER){ this.labelEdge = this.EDGE.TOP; }
+		if ( this.isHorizontal && (this.labelEdge == this.EDGE.LEFT  )){ this.labelEdge = this.EDGE.TOP; }
+		if ( this.isHorizontal && (this.labelEdge == this.EDGE.RIGHT )){ this.labelEdge = this.EDGE.TOP; }
+		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);
+		this.proximityBottom = this.itemHeight * (this.effectUnits - 0.5);
+
+		if (this.anchorEdge == this.EDGE.LEFT){
+			this.proximityLeft = 0;
+		}
+		if (this.anchorEdge == this.EDGE.RIGHT){
+			this.proximityRight = 0;
+		}
+		if (this.anchorEdge == this.EDGE.TOP){
+			this.proximityTop = 0;
+		}
+		if (this.anchorEdge == this.EDGE.BOTTOM){
+			this.proximityBottom = 0;
+		}
+		if (this.anchorEdge == this.EDGE.CENTER){
+			this.proximityLeft   /= 2;
+			this.proximityRight  /= 2;
+			this.proximityTop    /= 2;
+			this.proximityBottom /= 2;
+		}
+	},
+	
+	postCreate: function(args, frag) {
+		this.initializePositioning();
+
+		//
+		// 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");
+		dojo.event.connect(this, "addChild", this, "initializePositioning");
+	},
+
+	initializePositioning: function(){
+		this.itemCount = this.children.length;
+
+		this.barWidth  = (this.isHorizontal ? this.itemCount : 1) * this.itemWidth;
+		this.barHeight = (this.isHorizontal ? 1 : this.itemCount) * this.itemHeight;
+
+		this.totalWidth  = this.proximityLeft + this.proximityRight  + this.barWidth;
+		this.totalHeight = this.proximityTop  + this.proximityBottom + this.barHeight;
+
+		//
+		// 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);
+			this.children[i].posY = this.itemHeight * (this.isHorizontal ? 0 : i);
+
+			this.children[i].cenX = this.children[i].posX + (this.itemWidth  / 2);
+			this.children[i].cenY = this.children[i].posY + (this.itemHeight / 2);
+
+			var isz = this.isHorizontal ? this.itemWidth : this.itemHeight;
+			var r = this.effectUnits * isz;
+			var c = this.isHorizontal ? this.children[i].cenX : this.children[i].cenY;
+			var lhs = this.isHorizontal ? this.proximityLeft : this.proximityTop;
+			var rhs = this.isHorizontal ? this.proximityRight : this.proximityBottom;
+			var siz = this.isHorizontal ? this.barWidth : this.barHeight;
+
+			var range_lhs = r;
+			var range_rhs = r;
+
+			if (range_lhs > c+lhs){ range_lhs = c+lhs; }
+			if (range_rhs > (siz-c+rhs)){ range_rhs = siz-c+rhs; }
+
+			this.children[i].effectRangeLeft = range_lhs / isz;
+			this.children[i].effectRangeRght = range_rhs / isz;
+
+			//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
+		//
+		for (var i=0; i<this.children.length; i++){
+			var itm = this.children[i];
+			var elm = itm.domNode;
+			elm.style.left   = itm.posX + 'px';
+			elm.style.top    = itm.posY + 'px';
+			elm.style.width  = this.itemWidth + 'px';
+			elm.style.height = this.itemHeight + 'px';
+			
+			if ( itm.svgNode ) {
+				itm.svgNode.style.position = 'absolute';
+				itm.svgNode.style.left = this.itemPadding+'%';
+				itm.svgNode.style.top = this.itemPadding+'%';
+				itm.svgNode.style.width = (100 - 2 * this.itemPadding) + '%';
+				itm.svgNode.style.height = (100 - 2 * this.itemPadding) + '%';
+				itm.svgNode.style.zIndex = 1;
+	
+				itm.svgNode.setSize(this.itemWidth, this.itemHeight);
+			} else {
+				itm.imgNode.style.left = this.itemPadding+'%';
+				itm.imgNode.style.top = this.itemPadding+'%';
+				itm.imgNode.style.width = (100 - 2 * this.itemPadding) + '%';
+				itm.imgNode.style.height = (100 - 2 * this.itemPadding) + '%';
+			}
+		}
+
+		//
+		// calc the grid
+		//
+
+		this.calcHitGrid();
+	},
+
+	onBodyOut: function(e){
+		// clicking over an object inside of body causes this event to fire; ignore that case
+		if( dojo.html.overElement(dojo.body(), e) ){
+			return;
+		}
+		this.setDormant(e);
+	},
+
+	// when mouse moves out of menu's range
+	setDormant: function(e){
+		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");
+		}
+		this.onGridMouseMove(-1, -1);
+	},
+
+	// when mouse is moved into menu's range
+	setActive: function(e){
+		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");
+
+			this.timerScale=0.0;
+
+			// call mouse handler to do some initial necessary calculations/positioning
+			this.mouseHandler(e);
+
+			// slowly expand the icon size so it isn't jumpy
+			this.expandSlowly();
+		}
+	},
+
+	// when mouse is moved
+	mouseHandler: function(e) {
+		if ((e.pageX >= this.hitX1) && (e.pageX <= this.hitX2) &&
+			(e.pageY >= this.hitY1) && (e.pageY <= this.hitY2)){
+			if( !this.isOver ){
+				this.setActive(e);
+			}
+			this.onGridMouseMove(e.pageX-this.hitX1, e.pageY-this.hitY1);
+		}else{
+			if (this.isOver){
+				this.setDormant(e);
+			}
+		}
+	},
+
+	onResized: function() {
+		this.calcHitGrid();
+	},
+
+	onGridMouseMove: function(x, y){
+		this.pos = {x:x, y:y};
+		this.paint();
+	},
+	
+	paint: function(){
+		var x=this.pos.x;
+		var y=this.pos.y;
+
+		if( this.itemCount <= 0 ){ return; }
+
+		//
+		// 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;
+		var sim = this.isHorizontal ? 
+			(1.0-this.timerScale)*this.itemWidth + this.timerScale*this.itemMaxWidth :
+			(1.0-this.timerScale)*this.itemHeight + this.timerScale*this.itemMaxHeight ;
+
+		var cen = ((pos - prx) / siz) - 0.5;
+		var max_off_cen = (sim / siz) - 0.5;
+
+		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){
+			var cen2 = (y - this.proximityTop) / this.itemHeight;
+			off_weight = (cen2 > 0.5) ? 1 : y / (this.proximityTop + (this.itemHeight / 2));
+		}
+		if (this.anchorEdge == this.EDGE.TOP){
+			var cen2 = (y - this.proximityTop) / this.itemHeight;
+			off_weight = (cen2 < 0.5) ? 1 : (this.totalHeight - y) / (this.proximityBottom + (this.itemHeight / 2));
+		}
+		if (this.anchorEdge == this.EDGE.RIGHT){
+			var cen2 = (x - this.proximityLeft) / this.itemWidth;
+			off_weight = (cen2 > 0.5) ? 1 : x / (this.proximityLeft + (this.itemWidth / 2));
+		}
+		if (this.anchorEdge == this.EDGE.LEFT){
+			var cen2 = (x - this.proximityLeft) / this.itemWidth;
+			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{
+				off_weight = x / (this.totalWidth);
+			}
+
+			if (off_weight > 0.5){
+				off_weight = 1 - off_weight;
+			}
+
+			off_weight *= 2;
+		}
+
+
+		//
+		// set the sizes
+		//
+
+		for(var i=0; i<this.itemCount; i++){
+
+			var weight = this.weightAt(cen, i);
+
+			if (weight < 0){weight = 0;}
+
+			this.setitemsize(i, weight * off_weight);
+		}
+
+		//
+		// set the positions
+		//
+
+		var main_p = Math.round(cen);
+		var offset = 0;
+
+		if (cen < 0){
+			main_p = 0;
+
+		}else if (cen > this.itemCount - 1){
+
+			main_p = this.itemCount -1;
+
+		}else{
+
+			offset = (cen - main_p) * ((this.isHorizontal ? this.itemWidth : this.itemHeight) - this.children[main_p].sizeMain);
+		}
+
+		this.positionElementsFrom(main_p, offset);
+	},
+
+	weightAt: function(cen, 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);
+		}
+	},
+
+	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));
+
+		if (this.isHorizontal){
+
+			this.children[p].sizeW = w;
+			this.children[p].sizeH = h;
+
+			this.children[p].sizeMain = w;
+			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{
+
+			this.children[p].sizeW = w;
+			this.children[p].sizeH = h;
+
+			this.children[p].sizeOff  = w;
+			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);
+			}
+
+			this.children[p].domNode.style.left = x + 'px';
+			this.children[p].usualY = Math.round(this.children[p].cenY - (h / 2));
+
+			this.children[p].domNode.style.top  = this.children[p].usualY + 'px';
+		}
+
+		this.children[p].domNode.style.width  = w + 'px';
+		this.children[p].domNode.style.height = h + 'px';
+
+		if (this.children[p].svgNode){
+			this.children[p].svgNode.setSize(w, h);
+		}
+	},
+
+	positionElementsFrom: function(p, offset){
+
+		var pos = 0;
+
+		if (this.isHorizontal){
+			pos = Math.round(this.children[p].usualX + offset);
+			this.children[p].domNode.style.left = pos + 'px';
+		}else{
+			pos = Math.round(this.children[p].usualY + offset);
+			this.children[p].domNode.style.top = pos + 'px';
+		}
+		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){
+				this.children[i].domNode.style.left = bpos + 'px';
+			}else{
+				this.children[i].domNode.style.top = bpos + 'px';
+			}
+			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){
+				this.children[i].domNode.style.left = apos + 'px';
+			}else{
+				this.children[i].domNode.style.top = apos + 'px';
+			}
+			this.positionLabel(this.children[i]);
+		}
+
+	},
+
+	positionLabel: function(itm){
+
+		var x = 0;
+		var y = 0;
+		
+		var mb = dojo.html.getMarginBox(itm.lblNode);
+
+		if (this.labelEdge == this.EDGE.TOP){
+			x = Math.round((itm.sizeW / 2) - (mb.width / 2));
+			y = -mb.height;
+		}
+
+		if (this.labelEdge == this.EDGE.BOTTOM){
+			x = Math.round((itm.sizeW / 2) - (mb.width / 2));
+			y = itm.sizeH;
+		}
+
+		if (this.labelEdge == this.EDGE.LEFT){
+			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) - (mb.height / 2));
+		}
+
+		itm.lblNode.style.left = x + 'px';
+		itm.lblNode.style.top  = y + 'px';
+	},
+
+	calcHitGrid: function(){
+
+		var pos = dojo.html.getAbsolutePosition(this.domNode, true);
+
+		this.hitX1 = pos.x - this.proximityLeft;
+		this.hitY1 = pos.y - this.proximityTop;
+		this.hitX2 = this.hitX1 + this.totalWidth;
+		this.hitY2 = this.hitY1 + this.totalHeight;
+
+		//dojo.debug(this.hitX1+','+this.hitY1+' // '+this.hitX2+','+this.hitY2);
+	},
+
+	toEdge: function(inp, def){
+		return this.EDGE[inp.toUpperCase()] || def;
+	},
+	
+	// slowly expand the image to user specified max size
+	expandSlowly: function(){
+		if( !this.isOver ){ return; }
+		this.timerScale += 0.2;
+		this.paint();
+		if ( this.timerScale<1.0 ) {
+			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, "mouseHandler");
+		dojo.widget.FisheyeList.superclass.destroy.call(this);
+	}
+});
+
+dojo.widget.defineWidget(
+	"dojo.widget.FisheyeListItem",
+	dojo.widget.HtmlWidget,
+{
+	// Constructor arguments
+	iconSrc: "",
+	svgSrc: "",
+	caption: "",
+	// 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">' +
+		'  <img class="dojoHtmlFisheyeListItemImage" dojoAttachPoint="imgNode" dojoAttachEvent="onMouseOver;onMouseOut;onClick">' +
+		'  <div class="dojoHtmlFisheyeListItemLabel" dojoAttachPoint="lblNode"></div>' +
+		'</div>',
+	
+	imgNode: null,
+
+	fillInTemplate: function() {
+		//
+		// set image
+		// TODO: turn on/off SVG support based on browser version.
+		// this.parent.enableCrappySvgSupport is not available to this function
+		//
+		if (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)&&(!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();
+		} else {
+			if (dojo.dom.hasParent(this.imgNode) && this.id != ""){
+				var parent = this.imgNode.parentNode;
+				parent.setAttribute("id", this.id);
+			}
+			this.imgNode.src = this.iconSrc;
+		}
+
+		//
+		// Label
+		//
+		if ( this.lblNode ) {
+			this.lblNode.appendChild(document.createTextNode(this.caption));
+		}
+		dojo.html.disableSelection(this.domNode);
+	},
+	
+	createSvgNode: function(src){
+
+		var elm = document.createElement('embed');
+		elm.src = src;
+		elm.type = 'image/svg+xml';
+		//elm.style.border = '1px solid black';
+		elm.style.width = '1px';
+		elm.style.height = '1px';
+		elm.loaded = 0;
+		elm.setSizeOnLoad = false;
+
+		elm.onload = function(){
+			this.svgRoot = this.getSVGDocument().rootElement;
+			this.svgDoc = this.getSVGDocument().documentElement;
+			this.zeroWidth = this.svgRoot.width.baseVal.value;
+			this.zeroHeight = this.svgRoot.height.baseVal.value;
+			this.loaded = true;
+
+			if (this.setSizeOnLoad){
+				this.setSize(this.setWidth, this.setHeight);
+			}
+		}
+
+		elm.setSize = function(w, h){
+			if (!this.loaded){
+				this.setWidth = w;
+				this.setHeight = h;
+				this.setSizeOnLoad = true;
+				return;
+			}
+
+			this.style.width = w+'px';
+			this.style.height = h+'px';
+			this.svgRoot.width.baseVal.value = w;
+			this.svgRoot.height.baseVal.value = h;
+
+			var scale_x = w / this.zeroWidth;
+			var scale_y = h / this.zeroHeight;
+
+			for(var i=0; i<this.svgDoc.childNodes.length; i++){
+				if (this.svgDoc.childNodes[i].setAttribute){
+					this.svgDoc.childNodes[i].setAttribute( "transform", "scale("+scale_x+","+scale_y+")" );
+				}
+			}
+		}
+
+		return elm;
+	},
+
+	onMouseOver: function(e) {
+		// in conservative mode, don't activate the menu until user mouses over an icon
+		if( !this.parent.isOver ){
+			this.parent.setActive(e);
+		}
+		if ( this.caption != "" ) {
+			dojo.html.addClass(this.lblNode, "selected");
+			this.parent.positionLabel(this);
+		}
+	},
+	
+	onMouseOut: function() {
+		dojo.html.removeClass(this.lblNode, "selected");
+	},
+
+	onClick: function() {
+	}
+});
+

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FisheyeList.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FloatingPane.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FloatingPane.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FloatingPane.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FloatingPane.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,386 @@
+/*
+	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.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.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.dnd.HtmlDragMoveSource");
+dojo.require("dojo.dnd.HtmlDragMoveObject");
+dojo.require("dojo.widget.ResizeHandle");
+
+dojo.declare(
+	"dojo.widget.FloatingPaneBase",
+	null,
+	{
+		// Constructor arguments
+		title: '',
+		iconSrc: '',
+		hasShadow: false,
+		constrainToContainer: false,
+		taskBarId: "",
+		resizable: true,
+		titleBarDisplay: "fancy",
+
+		windowState: "normal",
+		displayCloseAction: false,
+		displayMinimizeAction: false,
+		displayMaximizeAction: false,
+
+		maxTaskBarConnectAttempts: 5,
+		taskBarConnectAttempts: 0,
+
+		templatePath: dojo.uri.dojoUri("src/widget/templates/FloatingPane.html"),
+		templateCssPath: dojo.uri.dojoUri("src/widget/templates/FloatingPane.css"),
+
+		drag: null,
+
+		fillInFloatingPaneTemplate: function(args, frag){
+			// 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!="none"){	
+				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(evt) {
+			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(evt) {
+			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(evt) {
+			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(){
+			if(this.windowState=="minimized"){
+				this.restoreWindow();
+			}else{
+				this.minimizeWindow();
+			}
+		},
+
+		closeWindow: function(evt) {
+			dojo.html.removeNode(this.domNode);
+			this.destroy();
+		},
+	
+		onMouseDown: function(evt) {
+			this.bringToTop();
+		},
+	
+		bringToTop: function() {
+			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";
+		},
+	
+		// 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);
+		},
+
+		showFloatingPane: function(){
+			this.bringToTop();
+		},
+
+		onFloatingPaneShow: function(){
+			var mb = dojo.html.getMarginBox(this.domNode);
+			this.resizeTo(mb.width, mb.height);
+		},
+	
+		// This is called when the user adjusts the size of the floating pane
+		resizeTo: function(w, h){
+			dojo.html.setMarginBox(this.domNode, { width: w, height: h });
+	
+			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(w, h); }
+			this.onResized();
+		},
+	
+		checkSize: function() {
+			// 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.
+		}
+	}
+);
+
+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();
+	}
+});
+
+dojo.require("dojo.widget.Dialog");
+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);
+		}
+	}
+);

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/FloatingPane.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/GoogleMap.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/GoogleMap.js?view=auto&rev=449122
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/GoogleMap.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/GoogleMap.js Fri Sep 22 16:22:30 2006
@@ -0,0 +1,208 @@
+/*
+	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.GoogleMap");
+dojo.require("dojo.event.*");
+dojo.require("dojo.math");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.widget.HtmlWidget");
+
+(function(){
+	var gkey = djConfig["gMapKey"]||djConfig["googleMapKey"];
+
+	//	the Google API key mechanism sucks.  We're hardcoding here for love and affection but I don't like it.
+	var uri=new dojo.uri.Uri(window.location.href);
+	if(uri.host=="www.dojotoolkit.org"){
+		gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hRqjp7ri2mNiOEYqetD3xnFHpt5rBSjszDd1sdufPyQKUTyCf_YxoIxvw";
+	}
+	else if(uri.host=="blog.dojotoolkit.org"){
+		gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hSkep6Av1xaMhVn3yCLkorJeXeLARQ6fammI_P3qSGleTJhoI5_1JmP_Q";
+	}
+	else if(uri.host=="archive.dojotoolkit.org"){
+		gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hTaQpDt0dyGLIHbXMPTzg1kWeAfwRTwZNyrUfbfxYE9yIvRivEjcXoDTg";
+	}
+	else if(uri.host=="dojotoolkit.org"){
+		gkey="ABQIAAAACUNdgv_7FGOmUslbm9l6_hSaOaO_TgJ5c3mtQFnk5JO2zD5dZBRZk-ieqVs7BORREYNzAERmcJoEjQ";
+	}
+
+	if(!dojo.hostenv.post_load_){
+		if(!gkey || gkey==""){
+			dojo.raise("dojo.widget.GoogleMap: The Google Map widget requires a proper API key in order to be used.");
+		}
+		var tag = "<scr"+"ipt src='http://maps.google.com/maps?file=api&amp;v=2&amp;key="+gkey+"'></scri"+"pt>";
+		if(!dj_global["GMap2"]){
+			document.write(tag);
+		}
+	}else{
+		dojo.debug("Cannot initialize Google Map system after the page has been loaded! Please either manually include the script block provided by Google in your page or require() the GoogleMap widget before onload has fired.");
+	}
+})();
+
+dojo.widget.defineWidget(
+	"dojo.widget.GoogleMap",
+	dojo.widget.HtmlWidget,
+	function(){
+		this.map=null;
+		this.geocoder=null;
+		this.data=[];
+		this.datasrc="";
+		this.controls=["largemap","scale","maptype"];
+	},
+{
+	templatePath:null,
+	templateCssPath:null,
+	isContainer: false,
+
+	_defaultPoint:{lat:39.10662, lng: -94.578209},
+
+	setControls:function(){
+		var methodmap={
+			largemap:GLargeMapControl,
+			smallmap:GSmallMapControl,
+			smallzoom:GSmallZoomControl,
+			scale:GScaleControl,
+			maptype:GMapTypeControl,
+			overview:GOverviewMapControl
+		};
+		for(var i=0; i<this.controls.length; i++){
+			this.map.addControl(new (methodmap[this.controls[i].toLowerCase()])());
+		}
+	},
+	
+	findCenter:function(bounds){
+		if(this.data.length==1){
+			return (new GLatLng(this.data[0].lat, this.data[0].lng));
+		}
+		var clat=(bounds.getNorthEast().lat()+bounds.getSouthWest().lat())/2;
+		var clng=(bounds.getNorthEast().lng()+bounds.getSouthWest().lng())/2;
+		return (new GLatLng(clat,clng));
+	},
+
+	createPinpoint:function(pt,overlay){
+		var m=new GMarker(pt);
+		if(overlay){
+			GEvent.addListener(m,"click",function(){
+				m.openInfoWindowHtml("<div>"+overlay+"</div>");
+			});
+		}
+		return m;
+	},
+	plot:function(obj){
+		var p=new GLatLng(obj.lat,obj.lng);
+		var d=obj.description||null;
+		var m=this.createPinpoint(p,d);
+		this.map.addOverlay(m);
+	},
+	plotAddress:function(address){
+		var self=this;
+		this.geocoder.getLocations(address, function(response){
+			if(!response || response.Status.code != 200){
+				alert("The address \"" + address + "\" was not found.");
+				return;
+			}
+			var obj={
+				lat:response.Placemark[0].Point.coordinates[1],
+				lng:response.Placemark[0].Point.coordinates[0],
+				description:response.Placemark[0].address
+			};
+			self.data.push(obj);
+			self.render();
+		});
+	},
+
+	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(){
+		if(this.data.length==0){
+			this.map.setCenter(new GLatLng(this._defaultPoint.lat, this._defaultPoint.lng), 4);
+			return;
+		}
+
+		//	remove all overlays
+		this.map.clearOverlays();
+
+		var bounds=new GLatLngBounds();
+		var d=this.data;
+		for(var i=0; i<d.length; i++){
+			bounds.extend(new GLatLng(d[i].lat,d[i].lng));
+		}
+		var zoom=Math.min((this.map.getBoundsZoomLevel(bounds)-1),14);
+		this.map.setCenter(this.findCenter(bounds), zoom);
+
+		for(var i=0; i<this.data.length; i++){
+			this.plot(this.data[i]);
+		}
+	},
+
+	initialize:function(args, frag){
+		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.domNode.style.position!="absolute"){
+			this.domNode.style.position="relative";
+		}
+		this.map=new GMap2(this.domNode);
+		try{
+			this.geocoder=new GClientGeocoder();
+		}catch(ex){}
+		this.render();
+		this.setControls();
+	}
+});

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/widget/GoogleMap.js
------------------------------------------------------------------------------
    svn:eol-style = native