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 [32/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...

Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DomWidget.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DomWidget.js?view=diff&rev=474551&r1=474550&r2=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DomWidget.js (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DomWidget.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
@@ -11,64 +11,65 @@
 dojo.provide("dojo.widget.DomWidget");
 
 dojo.require("dojo.event.*");
-dojo.require("dojo.string");
+dojo.require("dojo.io.*");
 dojo.require("dojo.widget.Widget");
 dojo.require("dojo.dom");
+dojo.require("dojo.html.style");
 dojo.require("dojo.xml.Parse");
 dojo.require("dojo.uri.*");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.extras");
 
 dojo.widget._cssFiles = {};
+dojo.widget._cssStrings = {};
+dojo.widget._templateCache = {};
 
+// Object: a mapping of strings that are used in template variable replacement
 dojo.widget.defaultStrings = {
 	dojoRoot: dojo.hostenv.getBaseScriptUri(),
 	baseScriptUri: dojo.hostenv.getBaseScriptUri()
 };
 
+dojo.widget.fillFromTemplateCache = function(	/*DomWidget*/				obj, 
+												/*String||dojo.uri.Uri*/	templatePath,
+												/*String, optional*/		templateString,
+												/*Boolean, optional*/		avoidCache){
+	// summary:
+	//		static method to build from a template w/ or w/o a real widget in
+	//		place
+	// obj: an instance of dojo.widget.DomWidget to initialize the template for
+	// templatePath: the URL to get the template from	
+	// templateString:
+	//		a string to use in lieu of fetching the template from a URL
+	// avoidCache:
+	//		should the template system not use whatever is in the cache and
+	//		always use the passed templatePath or templateString?
 
-// static method to build from a template w/ or w/o a real widget in place
-dojo.widget.buildFromTemplate = function(obj, templatePath, templateCssPath, templateString) {
+	// dojo.debug("avoidCache:", avoidCache);
 	var tpath = templatePath || obj.templatePath;
-	var cpath = templateCssPath || obj.templateCssPath;
 
-	if (!cpath && obj.templateCSSPath) {
-		obj.templateCssPath = cpath = obj.templateCSSPath;
-		obj.templateCSSPath = null;
-		dj_deprecated("templateCSSPath is deprecated, use templateCssPath");
-	}
-
-	// DEPRECATED: use Uri objects, not strings
-	if (tpath && !(tpath instanceof dojo.uri.Uri)) {
-		tpath = dojo.uri.dojoUri(tpath);
-		dj_deprecated("templatePath should be of type dojo.uri.Uri");
-	}
-	if (cpath && !(cpath instanceof dojo.uri.Uri)) {
-		cpath = dojo.uri.dojoUri(cpath);
-		dj_deprecated("templateCssPath should be of type dojo.uri.Uri");
-	}
-	
-	var tmplts = dojo.widget.DomWidget.templates;
+	var tmplts = dojo.widget._templateCache;
 	if(!obj["widgetType"]) { // don't have a real template here
 		do {
-			var dummyName = "__dummyTemplate__" + dojo.widget.buildFromTemplate.dummyCount++;
+			var dummyName = "__dummyTemplate__" + dojo.widget._templateCache.dummyCount++;
 		} while(tmplts[dummyName]);
 		obj.widgetType = dummyName;
 	}
+	var wt = obj.widgetType;
 
-	if((cpath)&&(!dojo.widget._cssFiles[cpath])){
-		dojo.html.insertCssFile(cpath);
-		obj.templateCssPath = null;
-		dojo.widget._cssFiles[cpath] = true;
-	}
-
-	var ts = tmplts[obj.widgetType];
+	var ts = tmplts[wt];
 	if(!ts){
-		tmplts[obj.widgetType] = {};
-		ts = tmplts[obj.widgetType];
+		tmplts[wt] = { "string": null, "node": null };
+		if(avoidCache){
+			ts = {};
+		}else{
+			ts = tmplts[wt];
+		}
 	}
-	if(!obj.templateString){
+	if((!obj.templateString)&&(!avoidCache)){
 		obj.templateString = templateString || ts["string"];
 	}
-	if(!obj.templateNode){
+	if((!obj.templateNode)&&(!avoidCache)){
 		obj.templateNode = ts["node"];
 	}
 	if((!obj.templateNode)&&(!obj.templateString)&&(tpath)){
@@ -76,6 +77,9 @@
 		// NOTE: we rely on blocking IO here!
 		var tstring = dojo.hostenv.getText(tpath);
 		if(tstring){
+			// strip <?xml ...?> declarations so that external SVG and XML
+			// documents can be added to a document without worry
+			tstring = tstring.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
 			var matches = tstring.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
 			if(matches){
 				tstring = matches[1];
@@ -84,19 +88,102 @@
 			tstring = "";
 		}
 		obj.templateString = tstring;
-		ts.string = tstring;
+		if(!avoidCache){
+			tmplts[wt]["string"] = tstring;
+		}
 	}
-	if(!ts["string"]) {
+	if((!ts["string"])&&(!avoidCache)){
 		ts.string = obj.templateString;
 	}
 }
-dojo.widget.buildFromTemplate.dummyCount = 0;
+dojo.widget._templateCache.dummyCount = 0;
 
+// Array: list of properties to search for node-to-property mappings
 dojo.widget.attachProperties = ["dojoAttachPoint", "id"];
+// String: name of the property to use for mapping DOM events to widget functions
 dojo.widget.eventAttachProperty = "dojoAttachEvent";
+// String: property name of code to evaluate when the widget is constructed
 dojo.widget.onBuildProperty = "dojoOnBuild";
+// Array:  possible accessibility values to set on widget elements - role or state
+dojo.widget.waiNames  = ["waiRole", "waiState"];
+// Object: Contains functions to set accessibility roles and states 
+// 	onto widget elements
+dojo.widget.wai = {
+	waiRole: { 	
+				// String: information for mapping accessibility role
+				name: "waiRole", 
+				// String: URI of the namespace for the set of roles
+				"namespace": "http://www.w3.org/TR/xhtml2", 
+				// String: alias to assign the namespace
+				alias: "x2",
+				// String: prefix to assign to the role value
+				prefix: "wairole:"
+	},
+	waiState: { 
+				// String: informatin for mapping accessibility state
+				name: "waiState", 
+				// String: URI of the namespace for the set of states
+				"namespace": "http://www.w3.org/2005/07/aaa", 
+				// String: alias to assign the namespace
+				alias: "aaa",
+				// String: empty string - state value does not require prefix
+				prefix: ""
+	},
+	setAttr: function(/*DomNode*/node, /*String*/ ns, /*String*/ attr, /*String|Boolean*/value){
+		// Summary: Use appropriate API to set the role or state attribute onto the element.
+		// Description: In IE use the generic setAttribute() api.  Append a namespace
+		//   alias to the attribute name and appropriate prefix to the value. 
+		//   Otherwise, use the setAttribueNS api to set the namespaced attribute. Also
+		//   add the appropriate prefix to the attribute value.
+		if(dojo.render.html.ie){
+			node.setAttribute(this[ns].alias+":"+ attr, this[ns].prefix+value);
+		}else{
+			node.setAttributeNS(this[ns]["namespace"], attr, this[ns].prefix+value);
+		}
+	},
+
+	getAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
+		// Summary:  Use the appropriate API to retrieve the role or state value
+		// Description: In IE use the generic getAttribute() api.  An alias value 
+		// 	was added to the attribute name to simulate a namespace when the attribute
+		//  was set.  Otherwise use the getAttributeNS() api to retrieve the state value
+		if(dojo.render.html.ie){
+			return node.getAttribute(this[ns].alias+":"+attr);
+		}else{
+			return node.getAttributeNS(this[ns]["namespace"], attr);
+		}
+	},
+	removeAttr: function(/*DomNode*/ node, /*String*/ ns, /*String|Boolena*/ attr){
+		// Summary:  Use the appropriate API to remove the role or state value
+		// Description: In IE use the generic removeAttribute() api.  An alias value 
+		// 	was added to the attribute name to simulate a namespace when the attribute
+		//  was set.  Otherwise use the removeAttributeNS() api to remove the state value
+		var success = true; //only IE returns a value
+		if(dojo.render.html.ie){
+			 success = node.removeAttribute(this[ns].alias+":"+attr);
+		}else{
+			node.removeAttributeNS(this[ns]["namespace"], attr);
+		}
+		return success;
+	}
+};
+
+dojo.widget.attachTemplateNodes = function(	/*DomNode*/		rootNode, 
+											/*Widget*/		targetObj, 
+											/*Array*/		events ){
+	// summary:
+	//		map widget properties and functions to the handlers specified in
+	//		the dom node and it's descendants. This function iterates over all
+	//		nodes and looks for these properties:
+	//			* dojoAttachPoint
+	//			* dojoAttachEvent	
+	//			* waiRole
+	//			* waiState
+	//			* any "dojoOn*" proprties passed in the events array
+	// rootNode:
+	//		the node to search for properties. All children will be searched.
+	// events: a list of properties generated from getDojoEventsFromStr.
 
-dojo.widget.attachTemplateNodes = function(rootNode, targetObj, events){
 	// FIXME: this method is still taking WAAAY too long. We need ways of optimizing:
 	//	a.) what we are looking for on each node
 	//	b.) the nodes that are subject to interrogation (use xpath instead?)
@@ -104,6 +191,10 @@
 	// var start = new Date();
 	var elementNodeType = dojo.dom.ELEMENT_NODE;
 
+	function trim(str){
+		return str.replace(/^\s+|\s+$/g, "");
+	}
+
 	if(!rootNode){ 
 		rootNode = targetObj.domNode;
 	}
@@ -113,24 +204,85 @@
 	}
 	// alert(events.length);
 
-	var nodes = rootNode.getElementsByTagName("*");
+	var nodes = rootNode.all || rootNode.getElementsByTagName("*");
 	var _this = targetObj;
 	for(var x=-1; x<nodes.length; x++){
 		var baseNode = (x == -1) ? rootNode : nodes[x];
-		// FIXME: is this going to have capitalization problems?
+		// FIXME: is this going to have capitalization problems?  Could use getAttribute(name, 0); to get attributes case-insensitve
 		var attachPoint = [];
-		for(var y=0; y<this.attachProperties.length; y++){
-			var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
-			if(tmpAttachPoint){
-				attachPoint = tmpAttachPoint.split(";");
-				for(var z=0; z<this.attachProperties.length; z++){
-					if((targetObj[attachPoint[z]])&&(dojo.lang.isArray(targetObj[attachPoint[z]]))){
-						targetObj[attachPoint[z]].push(baseNode);
-					}else{
-						targetObj[attachPoint[z]]=baseNode;
+		if(!targetObj.widgetsInTemplate || !baseNode.getAttribute('dojoType')){
+			for(var y=0; y<this.attachProperties.length; y++){
+				var tmpAttachPoint = baseNode.getAttribute(this.attachProperties[y]);
+				if(tmpAttachPoint){
+					attachPoint = tmpAttachPoint.split(";");
+					for(var z=0; z<attachPoint.length; z++){
+						if(dojo.lang.isArray(targetObj[attachPoint[z]])){
+							targetObj[attachPoint[z]].push(baseNode);
+						}else{
+							targetObj[attachPoint[z]]=baseNode;
+						}
+					}
+					break;
+				}
+			}
+
+			var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
+			if(attachEvent){
+				// NOTE: we want to support attributes that have the form
+				// "domEvent: nativeEvent; ..."
+				var evts = attachEvent.split(";");
+				for(var y=0; y<evts.length; y++){
+					if((!evts[y])||(!evts[y].length)){ continue; }
+					var thisFunc = null;
+					var tevt = trim(evts[y]);
+					if(evts[y].indexOf(":") >= 0){
+						// oh, if only JS had tuple assignment
+						var funcNameArr = tevt.split(":");
+						tevt = trim(funcNameArr[0]);
+						thisFunc = trim(funcNameArr[1]);
+					}
+					if(!thisFunc){
+						thisFunc = tevt;
+					}
+	
+					var tf = function(){ 
+						var ntf = new String(thisFunc);
+						return function(evt){
+							if(_this[ntf]){
+								_this[ntf](dojo.event.browser.fixEvent(evt, this));
+							}
+						};
+					}();
+					dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
+					// dojo.event.browser.addListener(baseNode, tevt, dojo.lang.hitch(_this, thisFunc));
+				}
+			}
+	
+			for(var y=0; y<events.length; y++){
+				//alert(events[x]);
+				var evtVal = baseNode.getAttribute(events[y]);
+				if((evtVal)&&(evtVal.length)){
+					var thisFunc = null;
+					var domEvt = events[y].substr(4); // clober the "dojo" prefix
+					thisFunc = trim(evtVal);
+					var funcs = [thisFunc];
+					if(thisFunc.indexOf(";")>=0){
+						funcs = dojo.lang.map(thisFunc.split(";"), trim);
+					}
+					for(var z=0; z<funcs.length; z++){
+						if(!funcs[z].length){ continue; }
+						var tf = function(){ 
+							var ntf = new String(funcs[z]);
+							return function(evt){
+								if(_this[ntf]){
+									_this[ntf](dojo.event.browser.fixEvent(evt, this));
+								}
+							}
+						}();
+						dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
+						// dojo.event.browser.addListener(baseNode, domEvt, dojo.lang.hitch(_this, funcs[z]));
 					}
 				}
-				break;
 			}
 		}
 		// continue;
@@ -142,76 +294,41 @@
 			targetObj[tmpltPoint]=baseNode;
 		}
 
-		var attachEvent = baseNode.getAttribute(this.eventAttachProperty);
-		if(attachEvent){
-			// NOTE: we want to support attributes that have the form
-			// "domEvent: nativeEvent; ..."
-			var evts = attachEvent.split(";");
-			for(var y=0; y<evts.length; y++){
-				if((!evts[y])||(!evts[y].length)){ continue; }
-				var thisFunc = null;
-				// var tevt = dojo.string.trim(evts[y]);
-				var tevt = dojo.string.trim(evts[y]);
-				if(evts[y].indexOf(":") >= 0){
-					// oh, if only JS had tuple assignment
-					var funcNameArr = tevt.split(":");
-					tevt = dojo.string.trim(funcNameArr[0]);
-					thisFunc = dojo.string.trim(funcNameArr[1]);
-				}
-				if(!thisFunc){
-					thisFunc = tevt;
-				}
-
-				var tf = function(){ 
-					var ntf = new String(thisFunc);
-					return function(evt){
-						if(_this[ntf]){
-							_this[ntf](dojo.event.browser.fixEvent(evt));
-						}
-					};
-				}();
-				dojo.event.browser.addListener(baseNode, tevt, tf, false, true);
-			}
-		}
-
-		for(var y=0; y<events.length; y++){
-			//alert(events[x]);
-			var evtVal = baseNode.getAttribute(events[y]);
-			if((evtVal)&&(evtVal.length)){
-				var thisFunc = null;
-				var domEvt = events[y].substr(4); // clober the "dojo" prefix
-				thisFunc = dojo.string.trim(evtVal);
-				var tf = function(){ 
-					var ntf = new String(thisFunc);
-					return function(evt){
-						if(_this[ntf]){
-							_this[ntf](dojo.event.browser.fixEvent(evt));
-						}
-					}
-				}();
-				dojo.event.browser.addListener(baseNode, domEvt, tf, false, true);
+		dojo.lang.forEach(dojo.widget.waiNames, function(name){
+			var wai = dojo.widget.wai[name];
+			var val = baseNode.getAttribute(wai.name);
+			if(val){
+				if(val.indexOf('-') == -1){ 
+					dojo.widget.wai.setAttr(baseNode, wai.name, "role", val);
+				}else{
+					// this is a state-value pair
+					var statePair = val.split('-');
+					dojo.widget.wai.setAttr(baseNode, wai.name, statePair[0], statePair[1]);
+				}
 			}
-		}
+		}, this);
 
 		var onBuild = baseNode.getAttribute(this.onBuildProperty);
 		if(onBuild){
 			eval("var node = baseNode; var widget = targetObj; "+onBuild);
 		}
-
-		// strip IDs to prevent dupes
-		baseNode.id = "";
 	}
 
 }
 
-dojo.widget.getDojoEventsFromStr = function(str){
+dojo.widget.getDojoEventsFromStr = function(/*String*/str){
+	// summary:
+	//		generates a list of properties with names that match the form
+	//		dojoOn*
+	// str: the template string to search
+	
 	// var lstr = str.toLowerCase();
 	var re = /(dojoOn([a-z]+)(\s?))=/gi;
 	var evts = str ? str.match(re)||[] : [];
 	var ret = [];
 	var lem = {};
 	for(var x=0; x<evts.length; x++){
-		if(evts[x].legth < 1){ continue; }
+		if(evts[x].length < 1){ continue; }
 		var cm = evts[x].replace(/\s/, "");
 		cm = (cm.slice(0, cm.length-1));
 		if(!lem[cm]){
@@ -219,316 +336,567 @@
 			ret.push(cm);
 		}
 	}
-	return ret;
+	return ret; // Array
 }
 
-
+/*
 dojo.widget.buildAndAttachTemplate = function(obj, templatePath, templateCssPath, templateString, targetObj) {
 	this.buildFromTemplate(obj, templatePath, templateCssPath, templateString);
 	var node = dojo.dom.createNodesFromText(obj.templateString, true)[0];
 	this.attachTemplateNodes(node, targetObj||obj, dojo.widget.getDojoEventsFromStr(templateString));
 	return node;
 }
+*/
 
-dojo.widget.DomWidget = function(){
-	dojo.widget.Widget.call(this);
-	if((arguments.length>0)&&(typeof arguments[0] == "object")){
-		this.create(arguments[0]);
-	}
-}
-dojo.inherits(dojo.widget.DomWidget, dojo.widget.Widget);
-
-dojo.lang.extend(dojo.widget.DomWidget, {
-	templateNode: null,
-	templateString: null,
-	preventClobber: false,
-	domNode: null, // this is our visible representation of the widget!
-	containerNode: null, // holds child elements
-
-	// Process the given child widget, inserting it's dom node as a child of our dom node
-	// FIXME: should we support addition at an index in the children arr and
-	// order the display accordingly? Right now we always append.
-	addChild: function(widget, overrideContainerNode, pos, ref, insertIndex){
-		if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems
-			dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
-			return null;
-		}else{
-			this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
-			this.registerChild(widget, insertIndex);
-		}
-		return widget;
-	},
-	
-	addWidgetAsDirectChild: function(widget, overrideContainerNode, pos, ref, insertIndex){
-		if((!this.containerNode)&&(!overrideContainerNode)){
-			this.containerNode = this.domNode;
-		}
-		var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
-		if(!pos){ pos = "after"; }
-		if(!ref){ ref = cn.lastChild; }
-		if(!insertIndex) { insertIndex = 0; }
-		widget.domNode.setAttribute("dojoinsertionindex", insertIndex);
-
-		// insert the child widget domNode directly underneath my domNode, in the
-		// specified position (by default, append to end)
-		if(!ref){
-			cn.appendChild(widget.domNode);
-		}else{
-			// FIXME: was this meant to be the (ugly hack) way to support insert @ index?
-			//dojo.dom[pos](widget.domNode, ref, insertIndex);
 
-			// CAL: this appears to be the intended way to insert a node at a given position...
-			if (pos == 'insertAtIndex'){
-				// dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild);
-				dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
+// summary:
+//		dojo.widget.DomWidget is the superclass that provides behavior for all
+//		DOM-based renderers, including HtmlWidget and SvgWidget. DomWidget
+//		implements the templating system that most widget authors use to define
+//		the UI for their widgets.
+dojo.declare("dojo.widget.DomWidget", 
+	dojo.widget.Widget,
+	function(){
+		if((arguments.length>0)&&(typeof arguments[0] == "object")){
+			this.create(arguments[0]);
+		}
+	},
+	{							 
+		// DomNode: a node that represents the widget template. Pre-empts both templateString and templatePath.
+		templateNode: null,
+
+		// String:
+		//		a string that represents the widget template. Pre-empts the
+		//		templatePath. In builds that have their strings "interned", the
+		//		templatePath is converted to an inline templateString, thereby
+		//		preventing a synchronous network call.
+		templateString: null,
+
+		// String:
+		//		a string that represents the CSS for the widgettemplate.
+		//		Pre-empts the templateCssPath. In builds that have their
+		//		strings "interned", the templateCssPath is converted to an
+		//		inline templateCssString, thereby preventing a synchronous
+		//		network call.
+		templateCssString: null,
+
+		// Boolean:
+		//		should the widget not replace the node from which it was
+		//		constructed? Widgets that apply behaviors to pre-existing parts
+		//		of a page can be implemented easily by setting this to "true".
+		//		In these cases, the domNode property will point to the node
+		//		which the widget was created from.
+		preventClobber: false,
+
+		// DomNode:
+		//		this is our visible representation of the widget! Other DOM
+		//		Nodes may by assigned to other properties, usually through the
+		//		template system's dojoAttachPonit syntax, but the domNode
+		//		property is the canonical "top level" node in widget UI.
+		domNode: null, 
+
+		// DomNode:
+		//		holds child elements. "containerNode" is generally set via a
+		//		dojoAttachPoint assignment and it designates where widgets that
+		//		are defined as "children" of the parent will be placed
+		//		visually.
+		containerNode: null,
+
+		// Boolean:
+		//		should we parse the template to find widgets that might be
+		//		declared in markup inside it? false by default.
+		widgetsInTemplate: false,
+
+		addChild: function(	/*Widget*/				widget, 
+							/*DomNode, optional*/	overrideContainerNode, 
+							/*String, optional*/	pos, 
+							/*DomNode, optional*/	ref,
+							/*int, optional*/		insertIndex){
+			// summary:
+			//		Process the given child widget, inserting it's dom node as
+			//		a child of our dom node
+			// overrideContainerNode: a non-default container node for the widget
+			// pos:
+			//		can be one of "before", "after", "first", or "last". This
+			//		has the same meaning as in dojo.dom.insertAtPosition()
+			// ref: a node to place the widget relative to
+			// insertIndex: DOM index, same meaning as in dojo.dom.insertAtIndex()
+
+			// FIXME: should we support addition at an index in the children arr and
+			// order the display accordingly? Right now we always append.
+			if(!this.isContainer){ // we aren't allowed to contain other widgets, it seems
+				dojo.debug("dojo.widget.DomWidget.addChild() attempted on non-container widget");
+				return null;
 			}else{
-				// dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild);
-				if((pos == "after")&&(ref === cn.lastChild)){
-					cn.appendChild(widget.domNode);
-				}else{
-					dojo.dom.insertAtPosition(widget.domNode, cn, pos);
+				if(insertIndex == undefined){
+					insertIndex = this.children.length;
 				}
+				this.addWidgetAsDirectChild(widget, overrideContainerNode, pos, ref, insertIndex);
+				this.registerChild(widget, insertIndex);
 			}
-		}
-	},
-
-	// Record that given widget descends from me
-	registerChild: function(widget, insertionIndex){
+			return widget; // Widget: the widget that was inserted
+		},
+		
+		addWidgetAsDirectChild: function(	/*Widget*/				widget, 
+											/*DomNode*/				overrideContainerNode, 
+											/*String, optional*/	pos, 
+											/*DomNode, optional*/	ref, 
+											/*int, optional*/		insertIndex){
+			// summary:
+			//		Process the given child widget, inserting it's dom node as
+			//		a child of our dom node
+			// overrideContainerNode: a non-default container node for the widget
+			// pos:
+			//		can be one of "before", "after", "first", or "last". This
+			//		has the same meaning as in dojo.dom.insertAtPosition()
+			// ref: a node to place the widget relative to
+			// insertIndex: DOM index, same meaning as in dojo.dom.insertAtIndex()
+			if((!this.containerNode)&&(!overrideContainerNode)){
+				this.containerNode = this.domNode;
+			}
+			var cn = (overrideContainerNode) ? overrideContainerNode : this.containerNode;
+			if(!pos){ pos = "after"; }
+			if(!ref){ 
+				if(!cn){ cn = dojo.body(); }
+				ref = cn.lastChild; 
+			}
+			if(!insertIndex) { insertIndex = 0; }
+			widget.domNode.setAttribute("dojoinsertionindex", insertIndex);
+
+			// insert the child widget domNode directly underneath my domNode, in the
+			// specified position (by default, append to end)
+			if(!ref){
+				cn.appendChild(widget.domNode);
+			}else{
+				// FIXME: was this meant to be the (ugly hack) way to support insert @ index?
+				//dojo.dom[pos](widget.domNode, ref, insertIndex);
 
-		// we need to insert the child at the right point in the parent's 
-		// 'children' array, based on the insertionIndex
+				// CAL: this appears to be the intended way to insert a node at a given position...
+				if (pos == 'insertAtIndex'){
+					// dojo.debug("idx:", insertIndex, "isLast:", ref === cn.lastChild);
+					dojo.dom.insertAtIndex(widget.domNode, ref.parentNode, insertIndex);
+				}else{
+					// dojo.debug("pos:", pos, "isLast:", ref === cn.lastChild);
+					if((pos == "after")&&(ref === cn.lastChild)){
+						cn.appendChild(widget.domNode);
+					}else{
+						dojo.dom.insertAtPosition(widget.domNode, cn, pos);
+					}
+				}
+			}
+		},
 
-		widget.dojoInsertionIndex = insertionIndex;
+		registerChild: function(/*Widget*/widget, /*int*/insertionIndex){
+			// summary: record that given widget descends from me
+			// widget: the widget that is now a child
+			// inesrtionIndex: where in the children[] array to place it
+
+			// we need to insert the child at the right point in the parent's 
+			// 'children' array, based on the insertionIndex
+
+			widget.dojoInsertionIndex = insertionIndex;
+
+			var idx = -1;
+			for(var i=0; i<this.children.length; i++){
+
+				//This appears to fix an out of order issue in the case of mixed
+				//markup and programmatically added children.  Previously, if a child
+				//existed from markup, and another child was addChild()d without specifying
+				//any additional parameters, it would end up first in the list, when in fact
+				//it should be after.  I can't see cases where this would break things, but
+				//I could see no other obvious solution. -dustin
 
-		var idx = -1;
-		for(var i=0; i<this.children.length; i++){
-			if (this.children[i].dojoInsertionIndex < insertionIndex){
-				idx = i;
+				if (this.children[i].dojoInsertionIndex <= insertionIndex){
+					idx = i;
+				}
 			}
-		}
 
-		this.children.splice(idx+1, 0, widget);
+			this.children.splice(idx+1, 0, widget);
 
-		widget.parent = this;
-		widget.addedTo(this);
+			widget.parent = this;
+			widget.addedTo(this, idx+1);
+			
+			// If this widget was created programatically, then it was erroneously added
+			// to dojo.widget.manager.topWidgets.  Fix that here.
+			delete dojo.widget.manager.topWidgets[widget.widgetId];
+		},
+
+		removeChild: function(/*Widget*/widget){
+			// summary: detach child domNode from parent domNode
+			dojo.dom.removeNode(widget.domNode);
+
+			// remove child widget from parent widget 
+			return dojo.widget.DomWidget.superclass.removeChild.call(this, widget); // Widget
+		},
+
+		getFragNodeRef: function(/*Object*/frag){
+			// summary:
+			//		returns the source node, if any, that the widget was
+			//		declared from
+			// frag:
+			//		an opaque data structure generated by the first-pass parser
+			if(!frag){return null;} // null
+			if(!frag[this.getNamespacedType()]){
+				dojo.raise("Error: no frag for widget type " + this.getNamespacedType() 
+					+ ", id " + this.widgetId
+					+ " (maybe a widget has set it's type incorrectly)");
+			}
+			return frag[this.getNamespacedType()]["nodeRef"]; // DomNode
+		},
 		
-		// If this widget was created programatically, then it was erroneously added
-		// to dojo.widget.manager.topWidgets.  Fix that here.
-		delete dojo.widget.manager.topWidgets[widget.widgetId];
-	},
-
-	// FIXME: we really need to normalize how we do things WRT "destroy" vs. "remove"
-	removeChild: function(widget){
-		for(var x=0; x<this.children.length; x++){
-			if(this.children[x] === widget){
-				this.children.splice(x, 1);
-				break;
+		postInitialize: function(/*Object*/args, /*Object*/frag, /*Widget*/parentComp){
+			// summary:
+			//		Replace the source domNode with the generated dom
+			//		structure, and register the widget with its parent.
+			//		This is an implementation of the stub function defined in
+			//		dojo.widget.Widget.
+			
+			//dojo.profile.start(this.widgetType + " postInitialize");
+			
+			var sourceNodeRef = this.getFragNodeRef(frag);
+			// Stick my generated dom into the output tree
+			//alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML);
+			if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){
+				// Add my generated dom as a direct child of my parent widget
+				// This is important for generated widgets, and also cases where I am generating an
+				// <li> node that can't be inserted back into the original DOM tree
+				parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "",  args["dojoinsertionindex"], sourceNodeRef);
+			} else if (sourceNodeRef){
+				// Do in-place replacement of the my source node with my generated dom
+				if(this.domNode && (this.domNode !== sourceNodeRef)){
+					var oldNode = sourceNodeRef.parentNode.replaceChild(this.domNode, sourceNodeRef);
+				}
 			}
-		}
-		return widget;
-	},
 
-	getFragNodeRef: function(frag){
-		if( !frag["dojo:"+this.widgetType.toLowerCase()] ){
-			dojo.raise("Error: no frag for widget type " + this.widgetType +
-				", id " + this.widgetId + " (maybe a widget has set it's type incorrectly)");
-		}
-		return (frag ? frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"] : null);
-	},
+			// Register myself with my parent, or with the widget manager if
+			// I have no parent
+			// TODO: the code below erroneously adds all programatically generated widgets
+			// to topWidgets (since we don't know who the parent is until after creation finishes)
+			if ( parentComp ) {
+				parentComp.registerChild(this, args.dojoinsertionindex);
+			} else {
+				dojo.widget.manager.topWidgets[this.widgetId]=this;
+			}
+
+			if(this.widgetsInTemplate){
+				var parser = new dojo.xml.Parse();
+
+				var subContainerNode;
+				//TODO: use xpath here?
+				var subnodes = this.domNode.getElementsByTagName("*");
+				for(var i=0;i<subnodes.length;i++){
+					if(subnodes[i].getAttribute('dojoAttachPoint') == 'subContainerWidget'){
+						subContainerNode = subnodes[i];
+//						break;
+					}
+					if(subnodes[i].getAttribute('dojoType')){
+						subnodes[i].setAttribute('_isSubWidget', true);
+					}
+				}
+				if (this.isContainer && !this.containerNode){
+					//no containerNode is available, which means a widget is used as a container. find it here and move
+					//all dom nodes defined in the main html page as children of this.domNode into the actual container
+					//widget's node (at this point, the subwidgets defined in the template file is not parsed yet)
+					if(subContainerNode){
+						var src = this.getFragNodeRef(frag);
+						if (src){
+							dojo.dom.moveChildren(src, subContainerNode);
+							//do not need to follow children nodes in the main html page, as they
+							//will be dealt with in the subContainerWidget
+							frag['dojoDontFollow'] = true;
+						}
+					}else{
+						dojo.debug("No subContainerWidget node can be found in template file for widget "+this);
+					}
+				}
+
+				var templatefrag = parser.parseElement(this.domNode, null, true);
+				// createSubComponents not createComponents because frag has already been created
+				dojo.widget.getParser().createSubComponents(templatefrag, this);
 	
-	// Replace source domNode with generated dom structure, and register
-	// widget with parent.
-	postInitialize: function(args, frag, parentComp){
-		var sourceNodeRef = this.getFragNodeRef(frag);
-		// Stick my generated dom into the output tree
-		//alert(this.widgetId + ": replacing " + sourceNodeRef + " with " + this.domNode.innerHTML);
-		if (parentComp && (parentComp.snarfChildDomOutput || !sourceNodeRef)){
-			// Add my generated dom as a direct child of my parent widget
-			// This is important for generated widgets, and also cases where I am generating an
-			// <li> node that can't be inserted back into the original DOM tree
-			parentComp.addWidgetAsDirectChild(this, "", "insertAtIndex", "",  args["dojoinsertionindex"], sourceNodeRef);
-		} else if (sourceNodeRef){
-			// Do in-place replacement of the my source node with my generated dom
-			if(this.domNode && (this.domNode !== sourceNodeRef)){
-				var oldNode = sourceNodeRef.parentNode.replaceChild(this.domNode, sourceNodeRef);
+				//find all the sub widgets defined in the template file of this widget
+				var subwidgets = [];
+				var stack = [this];
+				var w;
+				while((w = stack.pop())){
+					for(var i = 0; i < w.children.length; i++){
+						var cwidget = w.children[i];
+						if(cwidget._processedSubWidgets || !cwidget.extraArgs['_issubwidget']){ continue; }
+						subwidgets.push(cwidget);
+						if(cwidget.isContainer){
+							stack.push(cwidget);
+						}
+					}
+				}
+	
+				//connect event to this widget/attach dom node
+				for(var i = 0; i < subwidgets.length; i++){
+					var widget = subwidgets[i];
+					if(widget._processedSubWidgets){
+						dojo.debug("This should not happen: widget._processedSubWidgets is already true!");
+						return;
+					}
+					widget._processedSubWidgets = true;
+					if(widget.extraArgs['dojoattachevent']){
+						var evts = widget.extraArgs['dojoattachevent'].split(";");
+						for(var j=0; j<evts.length; j++){
+							var thisFunc = null;
+							var tevt = dojo.string.trim(evts[j]);
+							if(tevt.indexOf(":") >= 0){
+								// oh, if only JS had tuple assignment
+								var funcNameArr = tevt.split(":");
+								tevt = dojo.string.trim(funcNameArr[0]);
+								thisFunc = dojo.string.trim(funcNameArr[1]);
+							}
+							if(!thisFunc){
+								thisFunc = tevt;
+							}
+							if(dojo.lang.isFunction(widget[tevt])){
+								dojo.event.kwConnect({
+									srcObj: widget, 
+									srcFunc: tevt, 
+									targetObj: this, 
+									targetFunc: thisFunc
+								});
+							}else{
+								alert(tevt+" is not a function in widget "+widget);
+							}
+						}
+					}
+	
+					if(widget.extraArgs['dojoattachpoint']){
+						//don't attach widget.domNode here, as we do not know which
+						//dom node we should connect to (in checkbox widget case, 
+						//it is inputNode). So we make the widget itself available
+						this[widget.extraArgs['dojoattachpoint']] = widget;
+					}
+				}
 			}
-		}
 
-		// Register myself with my parent, or with the widget manager if
-		// I have no parent
-		// TODO: the code below erroneously adds all programatically generated widgets
-		// to topWidgets (since we don't know who the parent is until after creation finishes)
-		if ( parentComp ) {
-			parentComp.registerChild(this, args.dojoinsertionindex);
-		} else {
-			dojo.widget.manager.topWidgets[this.widgetId]=this;
-		}
-
-		// Expand my children widgets
-		if(this.isContainer){
-			//alert("recurse from " + this.widgetId);
-			// build any sub-components with us as the parent
-			var fragParser = dojo.widget.getParser();
-			fragParser.createComponents(frag, this);
-		}
-	},
-
-	startResize: function(coords){
-		dj_unimplemented("dojo.widget.DomWidget.startResize");
-	},
-
-	updateResize: function(coords){
-		dj_unimplemented("dojo.widget.DomWidget.updateResize");
-	},
-
-	endResize: function(coords){
-		dj_unimplemented("dojo.widget.DomWidget.endResize");
-	},
+			//dojo.profile.end(this.widgetType + " postInitialize");
 
-	// method over-ride
-	buildRendering: function(args, frag){
-		// DOM widgets construct themselves from a template
-		var ts = dojo.widget.DomWidget.templates[this.widgetType];
-		if(	
-			(!this.preventClobber)&&(
-				(this.templatePath)||
-				(this.templateNode)||
-				(
-					(this["templateString"])&&(this.templateString.length) 
-				)||
-				(
-					(typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) )
+			// Expand my children widgets
+			/* dojoDontFollow is important for a very special case
+			 * basically if you have a widget that you instantiate from script
+			 * and that widget is a container, and it contains a reference to a parent
+			 * instance, the parser will start recursively parsing until the browser
+			 * complains.  So the solution is to set an initialization property of 
+			 * dojoDontFollow: true and then it won't recurse where it shouldn't
+			 */
+			if(this.isContainer && !frag["dojoDontFollow"]){
+				//alert("recurse from " + this.widgetId);
+				// build any sub-components with us as the parent
+				dojo.widget.getParser().createSubComponents(frag, this);
+			}
+		},
+
+		// method over-ride
+		buildRendering: function(/*Object*/args, /*Object*/frag){
+			// summary:
+			//		Construct the UI for this widget, generally from a
+			//		template. This can be over-ridden for custom UI creation to
+			//		to side-step the template system.  This is an
+			//		implementation of the stub function defined in
+			//		dojo.widget.Widget.
+
+			// DOM widgets construct themselves from a template
+			var ts = dojo.widget._templateCache[this.widgetType];
+			
+			// Handle style for this widget here, as even if templatePath
+			// is not set, style specified by templateCssString or templateCssPath
+			// should be applied. templateCssString has higher priority
+			// than templateCssPath
+			if(args["templatecsspath"]){
+				args["templateCssPath"] = args["templatecsspath"];
+			}
+			var cpath = args["templateCssPath"] || this.templateCssPath;
+			if(cpath && !dojo.widget._cssFiles[cpath.toString()]){
+				if((!this.templateCssString)&&(cpath)){
+					this.templateCssString = dojo.hostenv.getText(cpath);
+					this.templateCssPath = null;
+				}
+				dojo.widget._cssFiles[cpath.toString()] = true;
+			}
+		
+			if((this["templateCssString"])&&(!this.templateCssString["loaded"])){
+				dojo.html.insertCssText(this.templateCssString, null, cpath);
+				if(!this.templateCssString){ this.templateCssString = ""; }
+				this.templateCssString.loaded = true;
+			}
+			if(	
+				(!this.preventClobber)&&(
+					(this.templatePath)||
+					(this.templateNode)||
+					(
+						(this["templateString"])&&(this.templateString.length) 
+					)||
+					(
+						(typeof ts != "undefined")&&( (ts["string"])||(ts["node"]) )
+					)
 				)
-			)
-		){
-			// if it looks like we can build the thing from a template, do it!
-			this.buildFromTemplate(args, frag);
-		}else{
-			// otherwise, assign the DOM node that was the source of the widget
-			// parsing to be the root node
-			this.domNode = this.getFragNodeRef(frag);
-		}
-		this.fillInTemplate(args, frag); 	// this is where individual widgets
-											// will handle population of data
-											// from properties, remote data
-											// sets, etc.
-	},
-
-	buildFromTemplate: function(args, frag){
-		// var start = new Date();
-		// copy template properties if they're already set in the templates object
-		var ts = dojo.widget.DomWidget.templates[this.widgetType];
-		if(ts){
-			if(!this.templateString.length){
-				this.templateString = ts["string"];
-			}
-			if(!this.templateNode){
-				this.templateNode = ts["node"];
-			}
-		}
-		var matches = false;
-		var node = null;
-		var tstr = new String(this.templateString); 
-		// attempt to clone a template node, if there is one
-		if((!this.templateNode)&&(this.templateString)){
-			matches = this.templateString.match(/\$\{([^\}]+)\}/g);
-			if(matches) {
-				// if we do property replacement, don't create a templateNode
-				// to clone from.
-				var hash = this.strings || {};
-				// FIXME: should this hash of default replacements be cached in
-				// templateString?
-				for(var key in dojo.widget.defaultStrings) {
-					if(dojo.lang.isUndefined(hash[key])) {
-						hash[key] = dojo.widget.defaultStrings[key];
-					}
-				}
-				// FIXME: this is a lot of string munging. Can we make it faster?
-				for(var i = 0; i < matches.length; i++) {
-					var key = matches[i];
-					key = key.substring(2, key.length-1);
-					var kval = (key.substring(0, 5) == "this.") ? this[key.substring(5)] : hash[key];
-					var value;
-					if((kval)||(dojo.lang.isString(kval))){
-						value = (dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval;
-						tstr = tstr.replace(matches[i], value);
+			){
+				// if it looks like we can build the thing from a template, do it!
+				this.buildFromTemplate(args, frag);
+			}else{
+				// otherwise, assign the DOM node that was the source of the widget
+				// parsing to be the root node
+				this.domNode = this.getFragNodeRef(frag);
+			}
+			this.fillInTemplate(args, frag); 	// this is where individual widgets
+												// will handle population of data
+												// from properties, remote data
+												// sets, etc.
+	},
+
+		buildFromTemplate: function(/*Object*/args, /*Object*/frag){
+			// summary:
+			//		Called by buildRendering, creates the actual UI in a DomWidget.
+
+			// var start = new Date();
+			// copy template properties if they're already set in the templates object
+			// dojo.debug("buildFromTemplate:", this);
+			var avoidCache = false;
+			if(args["templatepath"]){
+				avoidCache = true;
+				args["templatePath"] = args["templatepath"];
+			}
+			dojo.widget.fillFromTemplateCache(	this, 
+												args["templatePath"], 
+												null,
+												avoidCache);
+			var ts = dojo.widget._templateCache[this.widgetType];
+			if((ts)&&(!avoidCache)){
+				if(!this.templateString.length){
+					this.templateString = ts["string"];
+				}
+				if(!this.templateNode){
+					this.templateNode = ts["node"];
+				}
+			}
+			var matches = false;
+			var node = null;
+			// var tstr = new String(this.templateString); 
+			var tstr = this.templateString; 
+			// attempt to clone a template node, if there is one
+			if((!this.templateNode)&&(this.templateString)){
+				matches = this.templateString.match(/\$\{([^\}]+)\}/g);
+				if(matches) {
+					// if we do property replacement, don't create a templateNode
+					// to clone from.
+					var hash = this.strings || {};
+					// FIXME: should this hash of default replacements be cached in
+					// templateString?
+					for(var key in dojo.widget.defaultStrings) {
+						if(dojo.lang.isUndefined(hash[key])) {
+							hash[key] = dojo.widget.defaultStrings[key];
+						}
+					}
+					// FIXME: this is a lot of string munging. Can we make it faster?
+					for(var i = 0; i < matches.length; i++) {
+						var key = matches[i];
+						key = key.substring(2, key.length-1);
+						var kval = (key.substring(0, 5) == "this.") ? dojo.lang.getObjPathValue(key.substring(5), this) : hash[key];
+						var value;
+						if((kval)||(dojo.lang.isString(kval))){
+							value = new String((dojo.lang.isFunction(kval)) ? kval.call(this, key, this.templateString) : kval);
+							// Safer substitution, see heading "Attribute values" in  
+							// http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2
+							while (value.indexOf("\"") > -1) {
+								value=value.replace("\"","&quot;");
+							}
+							tstr = tstr.replace(matches[i], value);
+						}
+					}
+				}else{
+					// otherwise, we are required to instantiate a copy of the template
+					// string if one is provided.
+					
+					// FIXME: need to be able to distinguish here what should be done
+					// or provide a generic interface across all DOM implementations
+					// FIMXE: this breaks if the template has whitespace as its first 
+					// characters
+					// node = this.createNodesFromText(this.templateString, true);
+					// this.templateNode = node[0].cloneNode(true); // we're optimistic here
+					this.templateNode = this.createNodesFromText(this.templateString, true)[0];
+					if(!avoidCache){
+						ts.node = this.templateNode;
 					}
 				}
+			}
+			if((!this.templateNode)&&(!matches)){ 
+				dojo.debug("DomWidget.buildFromTemplate: could not create template");
+				return false;
+			}else if(!matches){
+				node = this.templateNode.cloneNode(true);
+				if(!node){ return false; }
 			}else{
-				// otherwise, we are required to instantiate a copy of the template
-				// string if one is provided.
-				
-				// FIXME: need to be able to distinguish here what should be done
-				// or provide a generic interface across all DOM implementations
-				// FIMXE: this breaks if the template has whitespace as its first 
-				// characters
-				// node = this.createNodesFromText(this.templateString, true);
-				// this.templateNode = node[0].cloneNode(true); // we're optimistic here
-				this.templateNode = this.createNodesFromText(this.templateString, true)[0];
-				ts.node = this.templateNode;
-			}
-		}
-		if((!this.templateNode)&&(!matches)){ 
-			dojo.debug("weren't able to create template!");
-			return false;
-		}else if(!matches){
-			node = this.templateNode.cloneNode(true);
-			if(!node){ return false; }
-		}else{
-			node = this.createNodesFromText(tstr, true)[0];
-		}
+				node = this.createNodesFromText(tstr, true)[0];
+			}
 
-		// recurse through the node, looking for, and attaching to, our
-		// attachment points which should be defined on the template node.
+			// recurse through the node, looking for, and attaching to, our
+			// attachment points which should be defined on the template node.
 
-		this.domNode = node;
-		// dojo.profile.start("attachTemplateNodes");
-		this.attachTemplateNodes(this.domNode, this);
-		// dojo.profile.end("attachTemplateNodes");
+			this.domNode = node;
+			// dojo.profile.start("attachTemplateNodes");
+			this.attachTemplateNodes();
+			// dojo.profile.end("attachTemplateNodes");
 		
-		// relocate source contents to templated container node
-		// this.containerNode must be able to receive children, or exceptions will be thrown
-		if (this.isContainer && this.containerNode){
-			var src = this.getFragNodeRef(frag);
-			if (src){
-				dojo.dom.moveChildren(src, this.containerNode);
+			// relocate source contents to templated container node
+			// this.containerNode must be able to receive children, or exceptions will be thrown
+			if (this.isContainer && this.containerNode){
+				var src = this.getFragNodeRef(frag);
+				if (src){
+					dojo.dom.moveChildren(src, this.containerNode);
+				}
 			}
-		}
-	},
+		},
 
-	attachTemplateNodes: function(baseNode, targetObj){
-		if(!targetObj){ targetObj = this; }
-		return dojo.widget.attachTemplateNodes(baseNode, targetObj, 
-					dojo.widget.getDojoEventsFromStr(this.templateString));
-	},
-
-	fillInTemplate: function(){
-		// dj_unimplemented("dojo.widget.DomWidget.fillInTemplate");
-	},
-	
-	// method over-ride
-	destroyRendering: function(){
-		try{
-			var tempNode = this.domNode.parentNode.removeChild(this.domNode);
-			delete tempNode;
-		}catch(e){ /* squelch! */ }
-	},
-
-	// FIXME: method over-ride
-	cleanUp: function(){},
-	
-	getContainerHeight: function(){
-		// FIXME: the generic DOM widget shouldn't be using HTML utils!
-		return dojo.html.getInnerHeight(this.domNode.parentNode);
-	},
-
-	getContainerWidth: function(){
-		// FIXME: the generic DOM widget shouldn't be using HTML utils!
-		return dojo.html.getInnerWidth(this.domNode.parentNode);
-	},
+		attachTemplateNodes: function(/*DomNode*/baseNode, /*Widget*/targetObj){
+			// summary: 
+			//		hooks up event handlers and property/node linkages. Calls
+			//		dojo.widget.attachTemplateNodes to do all the hard work.
+			// baseNode: defaults to "this.domNode"
+			// targetObj: defaults to "this"
+			if(!baseNode){ baseNode = this.domNode; }
+			if(!targetObj){ targetObj = this; }
+			return dojo.widget.attachTemplateNodes(baseNode, targetObj, 
+						dojo.widget.getDojoEventsFromStr(this.templateString));
+		},
+
+		fillInTemplate: function(){
+			// summary:
+			//		stub function! sub-classes may use as a default UI
+			//		initializer function. The UI rendering will be available by
+			//		the time this is called from buildRendering. If
+			//		buildRendering is over-ridden, this function may not be
+			//		fired!
+			// dojo.unimplemented("dojo.widget.DomWidget.fillInTemplate");
+		},
+		
+		// method over-ride
+		destroyRendering: function(){
+			// summary: UI destructor
+			try{
+				delete this.domNode;
+			}catch(e){ /* squelch! */ }
+		},
 
-	createNodesFromText: function(){
-		dj_unimplemented("dojo.widget.DomWidget.createNodesFromText");
+		// FIXME: method over-ride
+		cleanUp: function(){},
+		
+		getContainerHeight: function(){
+			// summary: unimplemented!
+			dojo.unimplemented("dojo.widget.DomWidget.getContainerHeight");
+		},
+
+		getContainerWidth: function(){
+			// summary: unimplemented!
+			dojo.unimplemented("dojo.widget.DomWidget.getContainerWidth");
+		},
+
+		createNodesFromText: function(){
+			// summary: unimplemented!
+			dojo.unimplemented("dojo.widget.DomWidget.createNodesFromText");
+		}
 	}
-});
-dojo.widget.DomWidget.templates = {};
+);

Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownContainer.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownContainer.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownContainer.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownContainer.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,90 @@
+/*
+	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.DropdownContainer");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.PopupContainer");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.layout");
+dojo.require("dojo.html.display");
+dojo.require("dojo.html.iframe");
+dojo.require("dojo.html.util");
+
+// summary:
+//		dojo.widget.DropdownContainer provides an input box and a button for a dropdown.
+//		In subclass, the dropdown can be specified.
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownContainer",
+	dojo.widget.HtmlWidget,
+	{
+		// String: width of the input box
+		inputWidth: "7em",
+		// String: id of this widget
+		id: "",
+		// String: id of the input box
+		inputId: "",
+		// String: name of the input box
+		inputName: "",
+		// dojo.uri.Uri: icon for the dropdown button
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/combo_box_arrow.png"),
+		// dojo.uri.Uri: alt text for the dropdown button icon
+		iconAlt: "",
+
+		inputNode: null,
+		buttonNode: null,
+		containerNode: null,
+
+		// String: toggle property of the dropdown
+		containerToggle: "plain",
+		// Int: toggle duration property of the dropdown
+		containerToggleDuration: 150,
+		containerAnimInProgress: false,
+
+		templateString: '<span style="white-space:nowrap"><input type="hidden" name="" value="" dojoAttachPoint="valueNode" /><input name="" type="text" value="" style="vertical-align:middle;" dojoAttachPoint="inputNode" autocomplete="off" /> <img src="${this.iconURL}" alt="${this.iconAlt}" dojoAttachEvent="onclick: onIconClick" dojoAttachPoint="buttonNode" style="vertical-align:middle; cursor:pointer; cursor:hand" /></span>',
+		templateCssPath: "",
+
+		fillInTemplate: function(args, frag){
+			var source = this.getFragNodeRef(frag);
+
+			this.popup = dojo.widget.createWidget("PopupContainer", {toggle: this.containerToggle, toggleDuration: this.containerToggleDuration});
+
+			this.containerNode = this.popup.domNode;
+
+			this.domNode.appendChild(this.popup.domNode);
+			if(this.id) { this.domNode.id = this.id; }
+			if(this.inputId){ this.inputNode.id = this.inputId; }
+			if(this.inputName){ this.inputNode.name = this.inputName; }
+			this.inputNode.style.width = this.inputWidth;
+
+			dojo.event.connect(this.inputNode, "onchange", this, "onInputChange");
+		},
+
+		onIconClick: function(evt){
+			if(!this.isEnabled) return;
+			if(!this.popup.isShowingNow){
+				this.popup.open(this.inputNode, this, this.buttonNode);
+			}else{
+				this.popup.close();
+			}
+		},
+
+		hideContainer: function(){
+			// summary: hide the dropdown
+			if(this.popup.isShowingNow){
+				this.popup.close();
+			}
+		},
+
+		onInputChange: function(){
+			// summary: signal for changes in the input box
+		}
+	}
+);

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

Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownDatePicker.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownDatePicker.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownDatePicker.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownDatePicker.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,228 @@
+/*
+	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.DropdownDatePicker");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.DropdownContainer");
+dojo.require("dojo.widget.DatePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.date.format");
+dojo.require("dojo.date.serialize");
+dojo.require("dojo.string.common");
+dojo.require("dojo.i18n.common");
+dojo.requireLocalization("dojo.widget", "DropdownDatePicker");
+
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownDatePicker",
+	dojo.widget.DropdownContainer,
+	{
+		/*
+		summary: 
+			A form input for entering dates with a pop-up dojo.widget.DatePicker to aid in selection
+
+	 	description: 
+			This is DatePicker in a DropdownContainer, it supports all features of DatePicker.
+	
+			The value displayed in the widget is localized according to the default algorithm provided
+			in dojo.date.format and dojo.date.parse.  It is possible to customize the user-visible formatting
+			with either the formatLength or displayFormat attributes.  The value sent to the server is
+			typically a locale-independent value in a hidden field as defined by the name attribute.
+			RFC3339 representation is used by default, but other options are available with saveFormat.
+
+	 	usage: 
+	 	              var ddp = dojo.widget.createWidget("DropdownDatePicker", {},   
+	 	              dojo.byId("DropdownDatePickerNode")); 
+	 	 
+	 	              <input dojoType="DropdownDatePicker">
+		*/
+
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/dateIcon.gif"),
+		zIndex: "10",
+
+		//String
+		// 	Type of visible formatting used, appropriate to locale (choice of long, short, medium or full)
+		//  See dojo.date.format for details.
+		formatLength: "short",
+		//String
+		// 	Pattern used to display formatted date.  Setting this overrides the locale-specific settings
+		//  which are used by default.  See dojo.date.format for a reference which defines the formatting patterns.
+		displayFormat: "",
+		dateFormat: "", // deprecated in 0.5
+		//String
+		//	Formatting scheme used when submitting the form element.  This formatting is used in a hidden
+		//  field (name) intended for server use, and is therefore typically locale-independent.
+		//  By default, uses rfc3339 style date formatting (rfc)
+		//	Use a pattern string like displayFormat or one of the following:
+		//	rfc|iso|posix|unix
+		saveFormat: "",
+		//String|Date
+		//	form value property if =='today' will default to todays date
+		value: "", 
+		//String
+		// 	name of the form element, used to create a hidden field by this name for form element submission.
+		name: "",
+
+		// Implement various attributes from DatePicker
+
+		//Integer
+		//	total weeks to display default 
+		displayWeeks: 6, 
+		//Boolean
+		//	if true, weekly size of calendar changes to accomodate the month if false, 42 day format is used
+		adjustWeeks: false,
+		//String|Date
+		//	first available date in the calendar set
+		startDate: "1492-10-12",
+		//String|Date
+		//	last available date in the calendar set
+		endDate: "2941-10-12",
+		//Integer
+		//	adjusts the first day of the week 0==Sunday..6==Saturday
+		weekStartsOn: "",
+		//Boolean
+		//	disable all incremental controls, must pick a date in the current display
+		staticDisplay: false,
+		
+		postMixInProperties: function(localProperties, frag){
+			// summary: see dojo.widget.DomWidget
+
+			dojo.widget.DropdownDatePicker.superclass.postMixInProperties.apply(this, arguments);
+			var messages = dojo.i18n.getLocalization("dojo.widget", "DropdownDatePicker", this.lang);
+			this.iconAlt = messages.selectDate;
+			
+			if(typeof(this.value)=='string'&&this.value.toLowerCase()=='today'){
+				this.value = new Date();
+			}
+			if(this.value && isNaN(this.value)){
+				var orig = this.value;
+				this.value = dojo.date.fromRfc3339(this.value);
+				if(!this.value){this.value = new Date(orig); dojo.deprecated("dojo.widget.DropdownDatePicker", "date attributes must be passed in Rfc3339 format", "0.5");}
+			}
+			if(this.value && !isNaN(this.value)){
+				this.value = new Date(this.value);
+			}
+		},
+
+		fillInTemplate: function(args, frag){
+			// summary: see dojo.widget.DomWidget
+			dojo.widget.DropdownDatePicker.superclass.fillInTemplate.call(this, args, frag);
+			//attributes to be passed on to DatePicker
+			var dpArgs = {widgetContainerId: this.widgetId, lang: this.lang, value: this.value,
+				startDate: this.startDate, endDate: this.endDate, displayWeeks: this.displayWeeks,
+				weekStartsOn: this.weekStartsOn, adjustWeeks: this.adjustWeeks, staticDisplay: this.staticDisplay};
+
+			//build the args for DatePicker based on the public attributes of DropdownDatePicker
+			this.datePicker = dojo.widget.createWidget("DatePicker", dpArgs, this.containerNode, "child");
+			dojo.event.connect(this.datePicker, "onValueChanged", this, "onSetDate");
+			
+			if(this.value){
+				this.onSetDate();
+			}
+			this.containerNode.style.zIndex = this.zIndex;
+			this.containerNode.explodeClassName = "calendarBodyContainer";
+			this.valueNode.name=this.name;
+		},
+
+		getValue: function(){
+			// summary: return current date in RFC 3339 format
+			return this.valueNode.value; /*String*/
+		},
+
+		getDate: function(){
+			// summary: return current date as a Date object
+			return this.datePicker.value; /*Date*/
+		},
+
+		setValue: function(/*Date|String*/rfcDate){
+			//summary: set the current date from RFC 3339 formatted string or a date object, synonymous with setDate
+			this.setDate(rfcDate);
+		},
+
+		setDate: function(/*Date|String*/dateObj){
+		//summary: set the current date and update the UI
+			this.datePicker.setDate(dateObj);
+			this._synchValueNode();
+		},
+	
+		onSetDate: function(){
+			if(this.dateFormat){
+				dojo.deprecated("dojo.widget.DropdownDatePicker",
+				"Must use displayFormat attribute instead of dateFormat.  See dojo.date.format for specification.", "0.5");
+				this.inputNode.value = dojo.date.strftime(this.datePicker.value, this.dateFormat, this.lang);
+			}else{
+				this.inputNode.value = dojo.date.format(this.datePicker.value,
+					{formatLength:this.formatLength, datePattern:this.displayFormat, selector:'dateOnly', locale:this.lang});
+			}
+			if(this.value < this.datePicker.startDate||this.value>this.datePicker.endDate){
+				this.inputNode.value = "";
+			}
+			this._synchValueNode();
+			this.onValueChanged(this.getDate());
+			this.hideContainer();
+		},
+
+		onValueChanged: function(/*Date*/dateObj){
+		//summary: triggered when this.value is changed
+		},
+		
+		onInputChange: function(){
+			if(this.dateFormat){
+				dojo.deprecated("dojo.widget.DropdownDatePicker",
+				"Cannot parse user input.  Must use displayFormat attribute instead of dateFormat.  See dojo.date.format for specification.", "0.5");
+			}else{
+				var input = dojo.string.trim(this.inputNode.value);
+				if(input){
+					var inputDate = dojo.date.parse(input,
+							{formatLength:this.formatLength, datePattern:this.displayFormat, selector:'dateOnly', locale:this.lang});			
+					if(inputDate){
+						this.setDate(inputDate);
+					}
+				} else {
+					this.valueNode.value = input;
+				}
+			}
+			// If the date entered didn't parse, reset to the old date.  KISS, for now.
+			//TODO: usability?  should we provide more feedback somehow? an error notice?
+			// seems redundant to do this if the parse failed, but at least until we have validation,
+			// this will fix up the display of entries like 01/32/2006
+			if(input){ this.onSetDate(); }
+		},
+
+		_synchValueNode: function(){
+			var date = this.datePicker.value;
+			var value;
+			switch(this.saveFormat.toLowerCase()){
+				case "rfc": case "iso": case "":
+					value = dojo.date.toRfc3339(date, 'dateOnly');
+					break;
+				case "posix": case "unix":
+					value = Number(date);
+					break;
+				default:
+					value = dojo.date.format(date, {datePattern:this.saveFormat, selector:'dateOnly', locale:this.lang});
+			}
+			this.valueNode.value = value;
+		},
+		
+		enable: function() {
+			this.inputNode.disabled = false;
+			this.datePicker.enable();
+			dojo.widget.DropdownDatePicker.superclass.enable.apply(this, arguments);
+		},
+		
+		disable: function() {
+			this.inputNode.disabled = true;
+			this.datePicker.disable();
+			dojo.widget.DropdownDatePicker.superclass.disable.apply(this, arguments);
+		}
+	}
+);

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

Added: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownTimePicker.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownTimePicker.js?view=auto&rev=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownTimePicker.js (added)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/DropdownTimePicker.js Mon Nov 13 14:54:45 2006
@@ -0,0 +1,116 @@
+/*
+	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.DropdownTimePicker");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.DropdownContainer");
+dojo.require("dojo.widget.TimePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html.*");
+dojo.require("dojo.date.format");
+dojo.require("dojo.date.serialize");
+dojo.require("dojo.i18n.common");
+dojo.requireLocalization("dojo.widget", "DropdownTimePicker");
+
+// summary
+//	input box with a drop-down gui control, for setting the time (hours, minutes, seconds, am/pm) of an event
+dojo.widget.defineWidget(
+	"dojo.widget.DropdownTimePicker",
+	dojo.widget.DropdownContainer,
+	{
+		// URL
+		//	path of icon for button to display time picker widget
+		iconURL: dojo.uri.dojoUri("src/widget/templates/images/timeIcon.gif"),
+		
+		// Number
+		//	z-index of time picker widget
+		zIndex: "10",
+
+		// pattern used in display of formatted time.  Uses locale-specific format by default.  See dojo.date.format.
+		displayFormat: "",
+
+		// String
+		//	Deprecated. format string for how time is displayed in the input box using strftime, see dojo.date.strftime	
+		timeFormat: "",
+
+//FIXME: need saveFormat attribute support
+
+		// type of format appropriate to locale.  see dojo.date.format
+		formatLength: "short",
+
+		// String
+		//	time value in RFC3339 format (http://www.ietf.org/rfc/rfc3339.txt)
+		//	ex: 12:00
+		value: "",
+
+		postMixInProperties: function() {
+			dojo.widget.DropdownTimePicker.superclass.postMixInProperties.apply(this, arguments);
+			var messages = dojo.i18n.getLocalization("dojo.widget", "DropdownTimePicker", this.lang);
+			this.iconAlt = messages.selectTime;
+		},
+
+		fillInTemplate: function(){
+			dojo.widget.DropdownTimePicker.superclass.fillInTemplate.apply(this, arguments);
+
+			var timeProps = { widgetContainerId: this.widgetId, lang: this.lang };
+			this.timePicker = dojo.widget.createWidget("TimePicker", timeProps, this.containerNode, "child");
+			dojo.event.connect(this.timePicker, "onSetTime", this, "onSetTime");
+			dojo.event.connect(this.inputNode,  "onchange",  this, "onInputChange");
+			this.containerNode.style.zIndex = this.zIndex;
+			this.containerNode.explodeClassName = "timeBorder";
+			if(this.value){
+				this.timePicker.selectedTime.anyTime = false;
+				this.timePicker.setDateTime("2005-01-01T" + this.value);
+				this.timePicker.initData();
+				this.timePicker.initUI();
+				this.onSetTime();
+			}
+		},
+		
+		onSetTime: function(){
+			// summary: callback when user sets the time via the TimePicker widget
+			if(this.timePicker.selectedTime.anyTime){
+				this.inputNode.value = "";
+			}else if(this.timeFormat){
+				dojo.deprecated("dojo.widget.DropdownTimePicker",
+				"Must use displayFormat attribute instead of timeFormat.  See dojo.date.format for specification.", "0.5");
+				this.inputNode.value = dojo.date.strftime(this.timePicker.time, this.timeFormat, this.lang);
+			}else{
+				this.inputNode.value = dojo.date.format(this.timePicker.time,
+					{formatLength:this.formatLength, datePattern:this.displayFormat, selector:'timeOnly', locale:this.lang});
+			}
+
+			this.hideContainer();
+		},
+		
+		onInputChange: function(){
+			// summary: callback when the user has typed in a time value manually
+			this.timePicker.time = "2005-01-01T" + this.inputNode.value; //FIXME: i18n
+			this.timePicker.setDateTime(this.timePicker.time);
+			this.timePicker.initData();
+			this.timePicker.initUI();
+		},
+		
+		enable: function() {
+			// summary: enable this widget to accept user input
+			this.inputNode.disabled = false;
+			this.timePicker.enable();
+			dojo.widget.DropdownTimePicker.superclass.enable.apply(this, arguments);
+		},
+		
+		disable: function() {
+			// summary: lock this widget so that the user can't change the value
+			this.inputNode.disabled = true;
+			this.timePicker.disable();
+			dojo.widget.DropdownTimePicker.superclass.disable.apply(this, arguments);
+		}
+	}
+);

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

Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Editor.js
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Editor.js?view=diff&rev=474551&r1=474550&r2=474551
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Editor.js (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/static/dojo/src/widget/Editor.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
@@ -13,60 +13,79 @@
  * - test, bug fix, more features :)
 */
 dojo.provide("dojo.widget.Editor");
-dojo.provide("dojo.widget.html.Editor");
+dojo.deprecated("dojo.widget.Editor", "is replaced by dojo.widget.Editor2", "0.5");
+
+dojo.require("dojo.io.*");
 dojo.require("dojo.widget.*");
 dojo.require("dojo.widget.Toolbar");
 dojo.require("dojo.widget.RichText");
 dojo.require("dojo.widget.ColorPalette");
+dojo.require("dojo.string.extras");
 
 dojo.widget.tags.addParseTreeHandler("dojo:Editor");
 
-dojo.widget.html.Editor = function() {
+dojo.widget.Editor = function() {
 	dojo.widget.HtmlWidget.call(this);
 	this.contentFilters = [];
+	this._toolbars = [];
 }
-dojo.inherits(dojo.widget.html.Editor, dojo.widget.HtmlWidget);
+dojo.inherits(dojo.widget.Editor, dojo.widget.HtmlWidget);
 
-dojo.widget.html.Editor.itemGroups = {
+dojo.widget.Editor.itemGroups = {
 	textGroup: ["bold", "italic", "underline", "strikethrough"],
-	blockGroup: ["formatBlock", "fontName"],
+	blockGroup: ["formatBlock", "fontName", "fontSize"],
 	justifyGroup: ["justifyleft", "justifycenter", "justifyright"],
 	commandGroup: ["save", "cancel"],
 	colorGroup: ["forecolor", "hilitecolor"],
 	listGroup: ["insertorderedlist", "insertunorderedlist"],
 	indentGroup: ["outdent", "indent"],
-	linkGroup: ["createlink", "insertimage"]
+	linkGroup: ["createlink", "insertimage", "inserthorizontalrule"]
 };
 
-dojo.widget.html.Editor.formatBlockValues = {
+dojo.widget.Editor.formatBlockValues = {
 	"Normal": "p",
 	"Main heading": "h2",
 	"Sub heading": "h3",
-	"Sub sub headering": "h4",
+	"Sub sub heading": "h4",
 	"Preformatted": "pre"
 };
 
-dojo.widget.html.Editor.fontNameValues = {
+dojo.widget.Editor.fontNameValues = {
 	"Arial": "Arial, Helvetica, sans-serif",
 	"Verdana": "Verdana, sans-serif",
 	"Times New Roman": "Times New Roman, serif",
 	"Courier": "Courier New, monospace"
 };
 
-dojo.widget.html.Editor.defaultItems = [
-	"commandGroup", "|", "linkGroup", "|", "textGroup", "|", "justifyGroup", "|", "listGroup", "indentGroup", "|", "colorGroup"
+dojo.widget.Editor.fontSizeValues = {
+	"1 (8 pt)" : "1",
+	"2 (10 pt)": "2",
+	"3 (12 pt)": "3",
+	"4 (14 pt)": "4",
+	"5 (18 pt)": "5",
+	"6 (24 pt)": "6",
+	"7 (36 pt)": "7"
+};
+
+dojo.widget.Editor.defaultItems = [
+	"commandGroup", "|", "blockGroup", "|", "textGroup", "|", "colorGroup", "|", "justifyGroup", "|", "listGroup", "indentGroup", "|", "linkGroup"
 ];
 
 // ones we support by default without asking the RichText component
 // NOTE: you shouldn't put buttons like bold, italic, etc in here
-dojo.widget.html.Editor.supportedCommands = ["save", "cancel", "|", "-", "/", " "];
+dojo.widget.Editor.supportedCommands = ["save", "cancel", "|", "-", "/", " "];
 
-dojo.lang.extend(dojo.widget.html.Editor, {
+dojo.lang.extend(dojo.widget.Editor, {
 	widgetType: "Editor",
 
-	items: dojo.widget.html.Editor.defaultItems,
-	formatBlockItems: dojo.lang.shallowCopy(dojo.widget.html.Editor.formatBlockValues),
-	fontNameItems: dojo.lang.shallowCopy(dojo.widget.html.Editor.fontNameValues),
+	saveUrl: "",
+	saveMethod: "post",
+	saveArgName: "editorContent",
+	closeOnSave: false,
+	items: dojo.widget.Editor.defaultItems,
+	formatBlockItems: dojo.lang.shallowCopy(dojo.widget.Editor.formatBlockValues),
+	fontNameItems: dojo.lang.shallowCopy(dojo.widget.Editor.fontNameValues),
+	fontSizeItems: dojo.lang.shallowCopy(dojo.widget.Editor.fontSizeValues),
 
 	// used to get the properties of an item if it is given as a string
 	getItemProperties: function(name) {
@@ -109,6 +128,10 @@
 			case "fontname":
 				props.name = "fontName";
 				props.values = this.fontNameItems;
+
+			case "fontsize":
+				props.name = "fontSize";
+				props.values = this.fontSizeItems;
 		}
 		return props;
 	},
@@ -194,7 +217,7 @@
 
 	addToolbar: function(toolbar) {
 		this.initToolbar();
-		if(!(toolbar instanceof dojo.widget.html.Toolbar)) {
+		if(!(toolbar instanceof dojo.widget.Toolbar)) {
 			toolbar = dojo.widget.createWidget(this._toolbarType);
 		}
 		this._toolbarContainer.addChild(toolbar);
@@ -206,7 +229,7 @@
 		if(!tb) { tb = this._toolbars[0]; }
 		var cmd = ((item)&&(!dojo.lang.isUndefined(item["getValue"]))) ?  cmd = item["getValue"](): item;
 
-		var groups = dojo.widget.html.Editor.itemGroups;
+		var groups = dojo.widget.Editor.itemGroups;
 		if(item instanceof dojo.widget.ToolbarItem) {
 			tb.addChild(item);
 		} else if(groups[cmd]) {
@@ -221,7 +244,15 @@
 						worked = false;
 					}
 				}
-				if(btnGroup.length) {
+				if(btnGroup.length){
+					/*
+					// the addChild interface is assinine. Work around it.
+					var tprops = this.getItemProperties(cmd);
+					var tmpGroup = dojo.widget.createWidget("ToolbarButtonGroup", tprops);
+					dojo.debug(btnGroup);
+					dojo.event.connect(tmpGroup, "onClick", this, "_action");
+					dojo.event.connect(tmpGroup, "onChangeSelect", this, "_action");
+					*/
 					var btn = tb.addChild(btnGroup, null, this.getItemProperties(cmd));
 					dojo.event.connect(btn, "onClick", this, "_action");
 					dojo.event.connect(btn, "onChangeSelect", this, "_action");
@@ -260,12 +291,23 @@
 					dojo.event.connect(select, "onSetValue", dojo.lang.hitch(this, function(item, value) {
 						this.onAction("fontName", value);
 					}));
+				} else if(cmd == "fontsize") {
+					var select = dojo.widget.createWidget("ToolbarSelect", {
+						name: "fontSize",
+						values: this.fontSizeItems
+					});
+					tb.addChild(select);
+					dojo.event.connect(select, "onSetValue", dojo.lang.hitch(this, function(item, value) {
+						this.onAction("fontSize", value);
+					}));
 				} else if(dojo.lang.inArray(cmd, ["forecolor", "hilitecolor"])) {
 					var btn = tb.addChild(dojo.widget.createWidget("ToolbarColorDialog", this.getItemProperties(cmd)));
 					dojo.event.connect(btn, "onSetValue", this, "_setValue");
 				} else {
 					var btn = tb.addChild(this.getCommandImage(cmd), null, this.getItemProperties(cmd));
-					if(dojo.lang.inArray(cmd, ["save", "cancel"])) {
+					if(cmd == "save"){
+						dojo.event.connect(btn, "onClick", this, "_save");
+					}else if(cmd == "cancel"){
 						dojo.event.connect(btn, "onClick", this, "_close");
 					} else {
 						dojo.event.connect(btn, "onClick", this, "_action");
@@ -321,7 +363,7 @@
 		var items = this._toolbarContainer.getItems();
 		for(var i = 0; i < items.length; i++) {
 			var item = items[i];
-			if(item instanceof dojo.widget.html.ToolbarSeparator) { continue; }
+			if(item instanceof dojo.widget.ToolbarSeparator) { continue; }
 			var cmd = item._name;
 			if (cmd == "save" || cmd == "cancel") { continue; }
 			else if(cmd == "justifyGroup") {
@@ -376,14 +418,14 @@
 		}
 	},
 
-	supportedCommands: dojo.widget.html.Editor.supportedCommands.concat(),
+	supportedCommands: dojo.widget.Editor.supportedCommands.concat(),
 
 	isSupportedCommand: function(cmd) {
 		// FIXME: how do we check for ActiveX?
 		var yes = dojo.lang.inArray(cmd, this.supportedCommands);
 		if(!yes) {
 			try {
-				var richText = this._richText || dojo.widget.html.RichText.prototype;
+				var richText = this._richText || dojo.widget.HtmlRichText.prototype;
 				yes = richText.queryCommandAvailable(cmd);
 			} catch(E) {}
 		}
@@ -399,9 +441,6 @@
 	},
 
 	_action: function(e) {
-		// djConfig.isDebug = true;
-		// dojo.debug(e);
-		// dojo.debug(e.getValue());
 		this._fire("onAction", e.getValue());
 	},
 
@@ -409,6 +448,26 @@
 		this._fire("onAction", a.getValue(), b);
 	},
 
+	_save: function(e){
+		// FIXME: how should this behave when there's a larger form in play?
+		if(!this._richText.isClosed){
+			if(this.saveUrl.length){
+				var content = {};
+				content[this.saveArgName] = this.getHtml();
+				dojo.io.bind({
+					method: this.saveMethod,
+					url: this.saveUrl,
+					content: content
+				});
+			}else{
+				dojo.debug("please set a saveUrl for the editor");
+			}
+			if(this.closeOnSave){
+				this._richText.close(e.getName().toLowerCase() == "save");
+			}
+		}
+	},
+
 	_close: function(e) {
 		if(!this._richText.isClosed) {
 			this._richText.close(e.getName().toLowerCase() == "save");
@@ -450,7 +509,7 @@
 	},
 
 	getHtml: function(){
-		this._richText.contentFilters = this.contentFilters;
+		this._richText.contentFilters = this._richText.contentFilters.concat(this.contentFilters);
 		return this._richText.getEditorContent();
 	},
 
@@ -473,147 +532,3 @@
 	onCancel: function(){}
 });
 
-/*
-function dontRunMe() {
-function createToolbar() {
-	tick("createToolbar");
-	tick("ct-init");
-	tbCont = dojo.widget.createWidget("toolbarContainer");
-	tb = dojo.widget.createWidget("toolbar");
-	tbCont.addChild(tb);
-
-	var saveBtn = tb.addChild("Save");
-	dojo.event.connect(saveBtn, "onClick", function() { editor.close(true); });
-	var cancelBtn = tb.addChild("Cancel");
-	dojo.event.connect(cancelBtn, "onClick", function() { editor.close(false); });
-	tb.addChild("|");
-
-	var headings = dojo.widget.createWidget("ToolbarSelect", {
-		name: "formatBlock",
-		values: {
-			"Normal": "p",
-			"Main heading": "h2",
-			"Sub heading": "h3",
-			"Sub sub heading": "h4",
-			"Preformatted": "pre"
-		}
-	});
-	dojo.event.connect(headings, "onSetValue", function(item, val) {
-		editor.execCommand("formatBlock", val);
-	});
-	tb.addChild(headings);
-	tb.addChild("|");
-	tock("ct-init");
-
-	// toolbar layout (2 rows):
-	// Save Cancel | WikiWord | Link Img | Table
-	// Heading FontFace | B I U | Alignment | OL UL < > | FG BG
-	var rows = [
-		["save", "cancel", "|", "wikiword", "|", "createlink", "insertimage", "|", "table"],
-		["formatBlock", "font", "|", "bold", "italic", "underline", "|", "justification", "|", "ol", "ul", "forecolor", "hilitecolor"]
-	];
-
-	tick("command array");
-	var commands = [
-		{ values: ["bold", "italic", "underline", "strikethrough"], toggleItem: true },
-		{ values: [
-				dojo.widget.createWidget("ToolbarColorDialog", {toggleItem: true, name: "forecolor", icon: cmdImg("forecolor")}),
-				dojo.widget.createWidget("ToolbarColorDialog", {toggleItem: true, name: "hilitecolor", icon: cmdImg("hilitecolor")})
-		]},
-		{ values: ["justifyleft", "justifycenter", "justifyright"], name: "justify", defaultButton: "justifyleft", buttonGroup: true, preventDeselect: true },
-		{ values: ["createlink", "insertimage"] },
-		{ values: ["outdent", "indent"] },
-		{ values: ["insertorderedlist", "insertunorderedlist"], name: "list", buttonGroup: true },
-		{ values: ["undo", "redo"] },
-		{ values: ["wikiword"], title: "WikiWord" }
-	];
-	tock("command array");
-
-	tick("processCommands");
-	for(var i = 0; i < commands.length; i++) {
-		var set = commands[i];
-		var values = set.values;
-		var btnGroup = [set.name];
-		for(var j = 0; j < values.length; j++) {
-			if(typeof values[j] == "string") {
-				var cmd = values[j];
-				if(cmd == "wikiword") {
-					var btn = tb.addChild(cmdImg(cmd), null, {name:cmd, label:"WikiWord"});
-					//dojo.event.connect(bt, "onClick", listenWikiWord);
-					//dojo.event.connect(bt, "onChangeSelect", listenWikiWord);
-				} else if(dojo.widget.html.RichText.prototype.queryCommandAvailable(cmd)) {
-					if(set.buttonGroup) {
-						btnGroup.push(cmdImg(cmd));
-					} else {
-						var btn = tb.addChild(cmdImg(cmd), null, {name:cmd, toggleItem:set.toggleItem});
-						dojo.event.connect(btn, "onClick", listen);
-						dojo.event.connect(btn, "onChangeSelect", listen);
-					}
-				}
-			} else {
-				if(dojo.widget.html.RichText.prototype.queryCommandAvailable(values[j].getName())) {
-					var btn = tb.addChild(values[j]);
-					dojo.event.connect(btn, "onSetValue", colorListen, values[j].getName());
-				}
-			}
-		}
-		if(set.buttonGroup && btnGroup.length > 1) {
-			var btn = tb.addChild(btnGroup, null, {defaultButton:set.defaultButton});
-			dojo.event.connect(btn, "onClick", listen);
-			dojo.event.connect(btn, "onChangeSelect", listen);
-		}
-
-		if(i + 1 != commands.length
-			&& !(tb.children[tb.children.length-1] instanceof dojo.widget.html.ToolbarSeparator)) {
-			tb.addChild("|");
-		}
-	}
-	tock("processCommands");
-	tock("createToolbar");
-	return tbCont;
-}
-function cmdImg(cmd) {
-	return dojo.uri.dojoUri("src/widget/templates/buttons/" + cmd + ".gif");
-}
-function createWysiwyg(node) {
-	tick("createWysiwyg");
-	tick("editor");
-	editor = dojo.widget.createWidget("RichText", {}, node);
-	tock("editor");
-	dojo.event.connect(editor, "close", function(changed) {
-		if(changed) { setTimeout(save, 5); }
-		setTimeout(function() {
-			dojo.io.bind({
-				url: location,
-				content: {
-					edit: "0",
-					cancel: "Cancel"
-				},
-				handler: function() {
-					hideLoad();
-				}
-			});
-		}, 15);
-		finishEdit();
-	});
-	autolinkWikiWords(editor);
-	cleanupWord(editor);
-	//createToolbar();
-	dojo.event.connect(editor, "onDisplayChanged", updateToolbar);
-
-	if(editor && tbCont && tb) {
-		var top = document.getElementById("jot-topbar");
-		dojo.html.addClass(top, "hidden");
-		//placeToolbar(tbCont.domNode);
-		//top.appendChild(tbCont.domNode);
-		//document.getElementById("jot-bottombar").innerHTML = "&nbsp;";
-	} else {
-		alert("Something went wrong trying to create the toolbar + editor.");
-	}
-	tock("createWysiwyg");
-	
-	return editor;
-}
-
-}
-*/