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/03/11 22:19:18 UTC

svn commit: r385171 [17/24] - in /jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo: ./ src/ src/animation/ src/collections/ src/crypto/ src/data/ src/dnd/ src/event/ src/flash/ src/flash/flash6/ src/flash/flash8/ src/fx/ src/grap...

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Parse.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Parse.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Parse.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Parse.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,319 @@
+/*
+	Copyright (c) 2004-2005, 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.Parse");
+
+dojo.require("dojo.widget.Manager");
+dojo.require("dojo.dom");
+
+dojo.widget.Parse = function(fragment) {
+	this.propertySetsList = [];
+	this.fragment = fragment;
+
+	/*	createComponents recurses over a raw JavaScript object structure,
+			and calls the corresponding handler for its normalized tagName if it exists
+	*/
+	this.createComponents = function(fragment, parentComp){
+		var djTags = dojo.widget.tags;
+		var returnValue = [];
+		// this allows us to parse without having to include the parent
+		// it is commented out as it currently breaks the existing mechanism for
+		// adding widgets programmatically.  Once that is fixed, this can be used
+		/*if( (fragment["tagName"])&&
+			(fragment != fragment["nodeRef"])){
+			var tn = new String(fragment["tagName"]);
+			// we split so that you can declare multiple
+			// non-destructive widgets from the same ctor node
+			var tna = tn.split(";");
+			for(var x=0; x<tna.length; x++){
+				var ltn = dojo.text.trim(tna[x]).toLowerCase();
+				if(djTags[ltn]){
+					fragment.tagName = ltn;
+					returnValue.push(djTags[ltn](fragment, this, parentComp, count++));
+				}else{
+					if(ltn.substr(0, 5)=="dojo:"){
+						dj_debug("no tag handler registed for type: ", ltn);
+					}
+				}
+			}
+		}*/
+		for(var item in fragment){
+			var built = false;
+			// if we have items to parse/create at this level, do it!
+			try{
+				if( fragment[item] && (fragment[item]["tagName"])&&
+					(fragment[item] != fragment["nodeRef"])){
+					var tn = new String(fragment[item]["tagName"]);
+					// we split so that you can declare multiple
+					// non-destructive widgets from the same ctor node
+					var tna = tn.split(";");
+					for(var x=0; x<tna.length; x++){
+						var ltn = (tna[x].replace(/^\s+|\s+$/g, "")).toLowerCase();
+						if(djTags[ltn]){
+							built = true;
+							// var tic = new Date();
+							fragment[item].tagName = ltn;
+							var ret = djTags[ltn](fragment[item], this, parentComp, fragment[item]["index"]);
+							returnValue.push(ret);
+						}else{
+							if((dojo.lang.isString(ltn))&&(ltn.substr(0, 5)=="dojo:")){
+								dojo.debug("no tag handler registed for type: ", ltn);
+							}
+						}
+					}
+				}
+			}catch(e){
+				dojo.debug("fragment creation error:", e);
+				// throw(e);
+				// IE is such a bitch sometimes
+			}
+
+			// if there's a sub-frag, build widgets from that too
+			if( (!built) && (typeof fragment[item] == "object")&&
+				(fragment[item] != fragment.nodeRef)&&
+				(fragment[item] != fragment["tagName"])){
+				returnValue.push(this.createComponents(fragment[item], parentComp));
+			}
+		}
+		return returnValue;
+	}
+
+	/*  parsePropertySets checks the top level of a raw JavaScript object
+			structure for any propertySets.  It stores an array of references to 
+			propertySets that it finds.
+	*/
+	this.parsePropertySets = function(fragment) {
+		return [];
+		var propertySets = [];
+		for(var item in fragment){
+			if(	(fragment[item]["tagName"] == "dojo:propertyset") ) {
+				propertySets.push(fragment[item]);
+			}
+		}
+		// FIXME: should we store these propertySets somewhere for later retrieval
+		this.propertySetsList.push(propertySets);
+		return propertySets;
+	}
+	
+	/*  parseProperties checks a raw JavaScript object structure for
+			properties, and returns an array of properties that it finds.
+	*/
+	this.parseProperties = function(fragment) {
+		var properties = {};
+		for(var item in fragment){
+			// FIXME: need to check for undefined?
+			// case: its a tagName or nodeRef
+			if((fragment[item] == fragment["tagName"])||
+				(fragment[item] == fragment.nodeRef)){
+				// do nothing
+			}else{
+				if((fragment[item]["tagName"])&&
+					(dojo.widget.tags[fragment[item].tagName.toLowerCase()])){
+					// TODO: it isn't a property or property set, it's a fragment, 
+					// so do something else
+					// FIXME: needs to be a better/stricter check
+					// TODO: handle xlink:href for external property sets
+				}else if((fragment[item][0])&&(fragment[item][0].value!="")&&(fragment[item][0].value!=null)){
+					try{
+						// FIXME: need to allow more than one provider
+						if(item.toLowerCase() == "dataprovider") {
+							var _this = this;
+							this.getDataProvider(_this, fragment[item][0].value);
+							properties.dataProvider = this.dataProvider;
+						}
+						properties[item] = fragment[item][0].value;
+						var nestedProperties = this.parseProperties(fragment[item]);
+						// FIXME: this kind of copying is expensive and inefficient!
+						for(var property in nestedProperties){
+							properties[property] = nestedProperties[property];
+						}
+					}catch(e){ dojo.debug(e); }
+				}
+			}
+		}
+		return properties;
+	}
+
+	/* getPropertySetById returns the propertySet that matches the provided id
+	*/
+	
+	this.getDataProvider = function(objRef, dataUrl) {
+		// FIXME: this is currently sync.  To make this async, we made need to move 
+		//this step into the widget ctor, so that it is loaded when it is needed 
+		// to populate the widget
+		dojo.io.bind({
+			url: dataUrl,
+			load: function(type, evaldObj){
+				if(type=="load"){
+					objRef.dataProvider = evaldObj;
+				}
+			},
+			mimetype: "text/javascript",
+			sync: true
+		});
+	}
+
+	
+	this.getPropertySetById = function(propertySetId){
+		for(var x = 0; x < this.propertySetsList.length; x++){
+			if(propertySetId == this.propertySetsList[x]["id"][0].value){
+				return this.propertySetsList[x];
+			}
+		}
+		return "";
+	}
+	
+	/* getPropertySetsByType returns the propertySet(s) that match(es) the
+	 * provided componentClass
+	 */
+	this.getPropertySetsByType = function(componentType){
+		var propertySets = [];
+		for(var x=0; x < this.propertySetsList.length; x++){
+			var cpl = this.propertySetsList[x];
+			var cpcc = cpl["componentClass"]||cpl["componentType"]||null;
+			if((cpcc)&&(propertySetId == cpcc[0].value)){
+				propertySets.push(cpl);
+			}
+		}
+		return propertySets;
+	}
+	
+	/* getPropertySets returns the propertySet for a given component fragment
+	*/
+	this.getPropertySets = function(fragment){
+		var ppl = "dojo:propertyproviderlist";
+		var propertySets = [];
+		var tagname = fragment["tagName"];
+		if(fragment[ppl]){ 
+			var propertyProviderIds = fragment[ppl].value.split(" ");
+			// FIXME: should the propertyProviderList attribute contain #
+			// 		  syntax for reference to ids or not?
+			// FIXME: need a better test to see if this is local or external
+			// FIXME: doesn't handle nested propertySets, or propertySets that
+			// 		  just contain information about css documents, etc.
+			for(propertySetId in propertyProviderIds){
+				if((propertySetId.indexOf("..")==-1)&&(propertySetId.indexOf("://")==-1)){
+					// get a reference to a propertySet within the current parsed structure
+					var propertySet = this.getPropertySetById(propertySetId);
+					if(propertySet != ""){
+						propertySets.push(propertySet);
+					}
+				}else{
+					// FIXME: add code to parse and return a propertySet from
+					// another document
+					// alex: is this even necessaray? Do we care? If so, why?
+				}
+			}
+		}
+		// we put the typed ones first so that the parsed ones override when
+		// iteration happens.
+		return (this.getPropertySetsByType(tagname)).concat(propertySets);
+	}
+	
+	/* 
+		nodeRef is the node to be replaced... in the future, we might want to add 
+		an alternative way to specify an insertion point
+
+		componentName is the expected dojo widget name, i.e. Button of ContextMenu
+
+		properties is an object of name value pairs
+	*/
+	this.createComponentFromScript = function(nodeRef, componentName, properties){
+		var ltn = "dojo:" + componentName.toLowerCase();
+		if(dojo.widget.tags[ltn]){
+			properties.fastMixIn = true;
+			return [dojo.widget.tags[ltn](properties, this, null, null, properties)];
+		}else{
+			if(ltn.substr(0, 5)=="dojo:"){
+				dojo.debug("no tag handler registed for type: ", ltn);
+			}
+		}
+	}
+}
+
+
+dojo.widget._parser_collection = {"dojo": new dojo.widget.Parse() };
+dojo.widget.getParser = function(name){
+	if(!name){ name = "dojo"; }
+	if(!this._parser_collection[name]){
+		this._parser_collection[name] = new dojo.widget.Parse();
+	}
+	return this._parser_collection[name];
+}
+
+/**
+ * Creates widget.
+ *
+ * @param name     The name of the widget to create
+ * @param props    Key-Value pairs of properties of the widget
+ * @param refNode  If the last argument is specified this node is used as
+ *                 a reference for inserting this node into a DOM tree else
+ *                 it beomces the domNode
+ * @param position The position to insert this widget's node relative to the
+ *                 refNode argument
+ * @return The new Widget object
+ */
+ 
+dojo.widget.createWidget = function (name, props, refNode, position) {
+
+	function fromScript (placeKeeperNode, name, props) {
+		var lowerCaseName = name.toLowerCase();
+		var namespacedName = "dojo:" + lowerCaseName;
+		props[namespacedName] = { 
+			dojotype: [{value: lowerCaseName}],
+			nodeRef: placeKeeperNode,
+			fastMixIn: true
+		};
+		return dojo.widget.getParser().createComponentFromScript(
+			placeKeeperNode, name, props, true);
+	}
+
+	if (typeof name != "string" && typeof props == "string") {
+		dojo.deprecated("dojo.widget.createWidget", 
+			"argument order is now of the form " +
+			"dojo.widget.createWidget(NAME, [PROPERTIES, [REFERENCENODE, [POSITION]]])");
+		return fromScript(name, props, refNode);
+	}
+	
+	props = props||{};
+	var notRef = false;
+	var tn = null;
+	var h = dojo.render.html.capable;
+	if(h){
+		tn = document.createElement("span");
+	}
+	if(!refNode){
+		notRef = true;
+		refNode = tn;
+		if(h){
+			document.body.appendChild(refNode);
+		}
+	}else if(position){
+		dojo.dom.insertAtPosition(tn, refNode, position);
+	}else{ // otherwise don't replace, but build in-place
+		tn = refNode;
+	}
+	var widgetArray = fromScript(tn, name, props);
+	if (!widgetArray[0] || typeof widgetArray[0].widgetType == "undefined") {
+		throw new Error("createWidget: Creation of \"" + name + "\" widget failed.");
+	}
+	if (notRef) {
+		if (widgetArray[0].domNode.parentNode) {
+			widgetArray[0].domNode.parentNode.removeChild(widgetArray[0].domNode);
+		}
+	}
+	return widgetArray[0]; // just return the widget
+}
+ 
+dojo.widget.fromScript = function(name, props, refNode, position){
+	dojo.deprecated("dojo.widget.fromScript", " use " +
+		"dojo.widget.createWidget instead");
+	return dojo.widget.createWidget(name, props, refNode, position);
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/PopUpButton.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/PopUpButton.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/PopUpButton.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/PopUpButton.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,192 @@
+/*
+	Copyright (c) 2004-2005, 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.PopUpButton");
+dojo.provide("dojo.widget.DomPopUpButton");
+dojo.provide("dojo.widget.HtmlPopUpButton");
+
+dojo.deprecated("dojo.widget.PopUpButton, dojo.widget.DomPopUpButton, dojo.widget.HtmlPopUpButton",  "use dojo.widget.DropDownButton2", "0.4");
+
+//dojo.require("dojo.widget.Button");
+//dojo.require("dojo.widget.HtmlButton");
+
+dojo.require("dojo.widget.Menu");
+dojo.require("dojo.widget.MenuItem");
+
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:PopUpButton");
+
+/* PopUpButton
+ **************/
+ 
+dojo.widget.PopUpButton = function () {
+	dojo.widget.PopUpButton.superclass.constructor.call(this);
+}
+dojo.inherits(dojo.widget.PopUpButton, dojo.widget.Widget);
+
+dojo.lang.extend(dojo.widget.PopUpButton, {
+	widgetType: "PopUpButton",
+	
+	label: ""
+});
+
+
+/* DomPopUpButton
+ *****************/
+dojo.widget.DomPopUpButton = function(){
+	dojo.widget.DomPopUpButton.superclass.constructor.call(this);
+}
+dojo.inherits(dojo.widget.DomPopUpButton, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.DomPopUpButton, {
+	widgetType: dojo.widget.PopUpButton.prototype.widgetType
+});
+
+
+/* HtmlPopUpButton
+ ******************/
+
+dojo.widget.HtmlPopUpButton = function () {
+	dojo.widget.HtmlPopUpButton.superclass.constructor.call(this);
+}
+dojo.inherits(dojo.widget.HtmlPopUpButton, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.HtmlPopUpButton, {
+	widgetType: dojo.widget.PopUpButton.prototype.widgetType,
+	templateString: null,
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/PopUpButton.css"),
+	
+	buildRendering: function (args, frag) {
+		dojo.style.insertCssFile(this.templateCssPath, null, true);
+	
+		this.domNode = document.createElement("a");
+		this.domNode.className = "PopUpButton";
+		dojo.event.connect(this.domNode, "onmousedown", this, "onMouseDown");
+		
+		// draw the arrow
+		var arrow = document.createElement("img");
+		arrow.src = dojo.uri.dojoUri("src/widget/templates/images/dropdownButtonsArrow.gif");
+		dojo.html.setClass(arrow, "downArrow");
+		this.domNode.appendChild(arrow);
+
+		this.menu = dojo.widget.fromScript("Menu");
+		dojo.html.addClass(this.menu.domNode, "PopUpButtonMenu");
+		dojo.event.connect(this.menu, "onSelect", this, "onSelect");
+		
+		if (frag["dojo:" + this.widgetType.toLowerCase()].nodeRef) {
+			var node = frag["dojo:" + this.widgetType.toLowerCase()].nodeRef;
+			var options = node.getElementsByTagName("option");
+			for (var i = 0; i < options.length; i++) {
+				var properties = {
+					title: dojo.dom.textContent(options[i]),
+					value: options[i].value
+				}
+				this.addItem(dojo.widget.fromScript("MenuItem", properties));
+			}
+		}
+	},
+
+	addItem: function (item) {
+		// TODO: should be dojo.widget.MenuItem
+		if (item instanceof dojo.widget.html.MenuItem) {
+			this.menu.push(item);
+		} else {
+			// TODO: create one
+			var menuItem = dojo.widget.fromScript("MenuItem", {title: item});
+			this.menu.push(menuItem);
+		}
+	},
+	
+	
+/* Enabled utility methods
+ **************************/
+	
+	_enabled: true,
+	
+	isEnabled: function() { return this._enabled; },
+	
+	setEnabled: function(enabled, force, preventEvent) {
+		enabled = Boolean(enabled);
+		if (force || this._enabled != enabled) {
+			this._enabled = enabled;
+			if (!preventEvent) {
+				this._fireEvent(this._enabled ? "onEnable" : "onDisable");
+				this._fireEvent("onChangeEnabled");
+			}
+		}
+		
+		dojo.html[(this._enabled ? "add" : "remove")
+			+ "Class"](this.domNode, "disabled");
+		
+		return this._enabled;
+	},
+	
+	enable: function(force, preventEvent) {
+		return this.setEnabled(true, force, preventEvent);
+	},
+	
+	disable: function(force, preventEvent) {
+		return this.setEnabled(false, force, preventEvent);
+	},
+	
+	toggleEnabled: function(force, preventEvent) {
+		return this.setEnabled(!this._enabled, force, preventEvent);
+	},
+
+
+/* Select utility methods
+ **************************/
+
+	onSelect: function (item, e) {
+		this.domNode.firstChild.nodeValue = item.title;
+	},
+	
+	onMouseDown: function (e) {
+		if (!this._menuVisible) {
+			this._showMenu(e);
+			dojo.lang.setTimeout(dojo.event.connect, 1, document, "onmousedown", this, "_hideMenu");
+		}
+	},
+	
+	
+	_fireEvent: function(evt) {
+		if(typeof this[evt] == "function") {
+			var args = [this];
+			for(var i = 1; i < arguments.length; i++) {
+				args.push(arguments[i]);
+			}
+			this[evt].apply(this, args);
+		}
+	},
+
+	
+	_showMenu: function (e) {
+		if (!this._enabled) { return; }
+		this._menuVisible = true;
+		with (dojo.html) {
+			var y = getAbsoluteY(this.domNode) + getInnerHeight(this.domNode);
+			var x = getAbsoluteX(this.domNode);
+		}
+	
+		document.body.appendChild(this.menu.domNode);
+		with (this.menu.domNode.style) {
+			top = y + "px";
+			left = x + "px";
+		}
+	},
+	
+	_hideMenu: function (e) {
+		this.menu.domNode.parentNode.removeChild(this.menu.domNode);
+		dojo.event.disconnect(document, "onmousedown", this, "_hideMenu");
+		this._menuVisible = false;
+	}
+
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,110 @@
+/*
+	Copyright (c) 2004-2005, 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.ResizableTextarea");
+dojo.require("dojo.html");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutPane");
+dojo.require("dojo.widget.ResizeHandle");
+
+dojo.widget.tags.addParseTreeHandler("dojo:resizabletextarea");
+
+dojo.widget.ResizableTextarea = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.ResizableTextarea, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.ResizableTextarea, {
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlResizableTextarea.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlResizableTextarea.css"),
+	widgetType: "ResizableTextarea",
+	tagName: "dojo:resizabletextarea",
+	isContainer: false,
+	textAreaNode: null,
+	textAreaContainer: null,
+	textAreaContainerNode: null,
+	statusBar: null,
+	statusBarContainerNode: null,
+	statusLabelNode: null,
+	statusLabel: null,
+	rootLayoutNode: null,
+	resizeHandleNode: null,
+	resizeHandle: null,
+
+	fillInTemplate: function(args, frag){
+		this.textAreaNode = this.getFragNodeRef(frag).cloneNode(true);
+
+		// FIXME: Safari apparently needs this!
+		document.body.appendChild(this.domNode);
+
+		this.rootLayout = dojo.widget.createWidget(
+			"LayoutPane",
+			{
+				minHeight: 50,
+				minWidth: 100
+			},
+			this.rootLayoutNode
+		);
+
+
+		this.textAreaContainer = dojo.widget.createWidget(
+			"LayoutPane",
+			{ layoutAlign: "client" },
+			this.textAreaContainerNode
+		);
+		this.rootLayout.addChild(this.textAreaContainer);
+
+		this.textAreaContainer.domNode.appendChild(this.textAreaNode);
+		with(this.textAreaNode.style){
+			width="100%";
+			height="100%";
+		}
+
+		this.statusBar = dojo.widget.createWidget(
+			"LayoutPane",
+			{ 
+				layoutAlign: "bottom", 
+				minHeight: 28
+			},
+			this.statusBarContainerNode
+		);
+		this.rootLayout.addChild(this.statusBar);
+
+		this.statusLabel = dojo.widget.createWidget(
+			"LayoutPane",
+			{ 
+				layoutAlign: "client", 
+				minWidth: 50
+			},
+			this.statusLabelNode
+		);
+		this.statusBar.addChild(this.statusLabel);
+
+		this.resizeHandle = dojo.widget.createWidget(
+			"ResizeHandle", 
+			{ targetElmId: this.rootLayout.widgetId },
+			this.resizeHandleNode
+		);
+		this.statusBar.addChild(this.resizeHandle);
+		// dojo.debug(this.rootLayout.widgetId);
+
+		// dojo.event.connect(this.resizeHandle, "beginSizing", this, "hideContent");
+		// dojo.event.connect(this.resizeHandle, "endSizing", this, "showContent");
+	},
+
+	hideContent: function(){
+		this.textAreaNode.style.display = "none";
+	},
+
+	showContent: function(){
+		this.textAreaNode.style.display = "";
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,93 @@
+/*
+	Copyright (c) 2004-2005, 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.ResizeHandle");
+dojo.provide("dojo.widget.html.ResizeHandle");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.event");
+
+dojo.widget.html.ResizeHandle = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.ResizeHandle, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.ResizeHandle, {
+	widgetType: "ResizeHandle",
+
+	isSizing: false,
+	startPoint: null,
+	startSize: null,
+
+	targetElmId: '',
+
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlResizeHandle.css"),
+	templateString: '<div class="dojoHtmlResizeHandle"><div></div></div>',
+
+	postCreate: function(){
+		dojo.event.connect(this.domNode, "onmousedown", this, "beginSizing");
+	},
+
+	beginSizing: function(e){
+		if (this.isSizing){ return false; }
+
+		this.targetElm = dojo.widget.byId(this.targetElmId);
+		if (!this.targetElm){ return; }
+
+		var screenX = window.event ? window.event.clientX : e.pageX;
+		var screenY = window.event ? window.event.clientY : e.pageY;
+
+		this.isSizing = true;
+		this.startPoint  = {'x':e.clientX, 'y':e.clientY};
+		this.startSize  = {'w':dojo.style.getOuterWidth(this.targetElm.domNode), 'h':dojo.style.getOuterHeight(this.targetElm.domNode)};
+
+		dojo.event.kwConnect({
+			srcObj: document.body, 
+			srcFunc: "onmousemove",
+			targetObj: this,
+			targetFunc: "changeSizing",
+			rate: 25
+		});
+		dojo.event.connect(document.body, "onmouseup", this, "endSizing");
+
+		e.preventDefault();
+	},
+
+	changeSizing: function(e){
+		// On IE, if you move the mouse above/to the left of the object being resized,
+		// sometimes clientX/Y aren't set, apparently.  Just ignore the event.
+		try{
+			if(!e.clientX  || !e.clientY){ return; }
+		}catch(e){
+			// sometimes you get an exception accessing above fields...
+			return;
+		}
+		var dx = this.startPoint.x - e.clientX;
+		var dy = this.startPoint.y - e.clientY;
+		this.targetElm.resizeTo(this.startSize.w - dx, this.startSize.h - dy);
+
+		e.preventDefault();
+	},
+
+	endSizing: function(e){
+		dojo.event.disconnect(document.body, "onmousemove", this, "changeSizing");
+		dojo.event.disconnect(document.body, "onmouseup", this, "endSizing");
+
+		this.isSizing = false;
+	}
+
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:ResizeHandle");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/RichText.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/RichText.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/RichText.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/RichText.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,1058 @@
+/*
+	Copyright (c) 2004-2005, 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.RichText");
+dojo.provide("dojo.widget.HtmlRichText");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.event.*");
+dojo.require("dojo.style");
+dojo.require("dojo.string");
+
+// used to save content
+try {
+	document.write('<textarea id="dojo.widget.RichText.savedContent" ' +
+		'style="display:none;position:absolute;top:-100px;left:-100px;"></textarea>');
+}catch(e){ }
+
+dojo.widget.tags.addParseTreeHandler("dojo:richtext");
+
+dojo.widget.HtmlRichText = function () {
+	dojo.widget.HtmlWidget.call(this);
+	this.contentFilters = [];
+}
+dojo.inherits(dojo.widget.HtmlRichText, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.HtmlRichText, {
+
+	widgetType: "richtext",
+
+	/** whether to inherit the parent's width or simply use 100% */
+	inheritWidth: false,
+	focusOnLoad: true,
+	
+	/**
+	 * If a save name is specified the content is saved and restored if the
+	 * editor is not properly closed after editing has started.
+	 */
+	saveName: "",
+	_content: "",
+	
+	/** The minimum height that the editor should have */
+	minHeight: "1em",
+	
+	isClosed: true,
+	isLoaded: false,
+	
+	/** whether to use the active-x object in IE */
+	useActiveX: false,
+	
+	_SEPARATOR: "@@**%%__RICHTEXTBOUNDRY__%%**@@",
+
+	// contentFilters: [],
+
+/* Init
+ *******/
+
+	fillInTemplate: function(){
+		this.open();
+
+		// add the formatting functions
+		var funcs = ["queryCommandEnabled", "queryCommandState",
+			"queryCommandValue", "execCommand"];
+		for (var i = 0; i < funcs.length; i++) {
+			dojo.event.connect("around", this, funcs[i], this, "_normalizeCommand");
+		}
+		
+		// backwards compatibility, needs to be removed
+		dojo.event.connect(this, "onKeyPressed", this, "afterKeyPress");
+		dojo.event.connect(this, "onKeyPress", this, "keyPress");
+		dojo.event.connect(this, "onKeyDown", this, "keyDown");
+		dojo.event.connect(this, "onKeyUp", this, "keyUp");
+	},
+
+	/**
+	 * Transforms the node referenced in this.domNode into a rich text editing
+	 * node. This can result in the creation and replacement with an <iframe> if
+	 * designMode is used, an <object> and active-x component if inside of IE or
+	 * a reguler element if contentEditable is available.
+	 */
+	open: function (element) {
+		dojo.event.topic.publish("dojo.widget.RichText::open", this);
+
+		if (!this.isClosed) { this.close(); }
+		this._content = "";
+		if (arguments.length == 1) { this.domNode = element; } // else unchanged
+		
+		if(this.domNode.nodeName.toLowerCase() == "textarea"){
+			this.textarea = this.domNode;
+			var html = dojo.string.trim(this.textarea.value);
+			if(html == ""){ html = "&nbsp;"; }
+			this.domNode = document.createElement("div");
+			with(this.textarea.style){
+				display = "block";
+				position = "absolute";
+				width = "1px";
+				height = "1px";
+				border = margin = padding = "0px";
+				visiblity = "hidden";
+			}
+			dojo.dom.insertBefore(this.domNode, this.textarea);
+			// this.domNode.innerHTML = html;
+			
+			if(this.textarea.form){
+				dojo.event.connect(this.textarea.form, "onsubmit", 
+					// FIXME: should we be calling close() here instead?
+					dojo.lang.hitch(this, function(){
+						this.textarea.value = this.getEditorContent();
+					})
+				);
+			}
+			
+			// dojo plucks our original domNode from the document so we need
+			// to go back and put ourselves back in
+			var editor = this;
+			dojo.event.connect(this, "postCreate", function (){
+				dojo.dom.insertAfter(editor.textarea, editor.domNode);
+			});
+		} else {
+			var html = dojo.string.trim(this.domNode.innerHTML);
+			if(html == ""){ html = "&nbsp;"; }
+		}
+				
+		this._oldHeight = dojo.style.getContentHeight(this.domNode);
+		this._oldWidth = dojo.style.getContentWidth(this.domNode);
+		
+		this.savedContent = document.createElement("div");
+		while (this.domNode.hasChildNodes()) {
+			this.savedContent.appendChild(this.domNode.firstChild);
+		}
+		
+		// If we're a list item we have to put in a blank line to force the
+		// bullet to nicely align at the top of text
+		if (this.domNode.nodeName == "LI") { this.domNode.innerHTML = " <br>"; }
+				
+		if (this.saveName != "") {
+			var saveTextarea = document.getElementById("dojo.widget.RichText.savedContent");
+			if (saveTextarea.value != "") {
+				var datas = saveTextarea.value.split(this._SEPARATOR);
+				for (var i = 0; i < datas.length; i++) {
+					var data = datas[i].split(":");
+					if (data[0] == this.saveName) {
+						html = data[1];
+						datas.splice(i, 1);
+						break;
+					}
+				}				
+			}
+			this.connect(window, "onunload", "_saveContent");
+		}
+
+		// Safari's selections go all out of whack if we do it inline,
+		// so for now IE is our only hero
+		//if (typeof document.body.contentEditable != "undefined") {
+		if (this.useActiveX && dojo.render.html.ie) { // active-x
+			this._drawObject(html);
+		} else if (dojo.render.html.ie) { // contentEditable, easy
+			this.editNode = document.createElement("div");
+			with (this.editNode) {
+				contentEditable = true;
+				innerHTML = html;
+				style.height = this.minHeight;
+			}
+			this.domNode.appendChild(this.editNode);
+			
+			var events = ["onBlur", "onFocus", "onKeyPress",
+				"onKeyDown", "onKeyUp", "onClick"];
+			for (var i = 0; i < events.length; i++) {
+				this.connect(this.editNode, events[i].toLowerCase(), events[i]);
+			}
+		
+			this.window = window;
+			this.document = document;
+			
+			this.onLoad();
+		} else { // designMode in iframe
+			this._drawIframe(html);
+		}
+
+		// TODO: this is a guess at the default line-height, kinda works
+		if (this.domNode.nodeName == "LI") { this.domNode.lastChild.style.marginTop = "-1.2em"; }
+		dojo.html.addClass(this.domNode, "RichTextEditable");
+		
+		this.isClosed = false;
+	},
+	
+	/** Draws an iFrame using the existing one if one exists. Used by Mozilla and Safari */
+	_drawIframe: function (html) {
+		if (!this.iframe) {
+			this.iframe = document.createElement("iframe");
+			with (this.iframe) {
+				scrolling = "no";
+				style.border = "none";
+				style.lineHeight = "0"; // squash line height
+				style.verticalAlign = "bottom";
+			}
+		}
+
+		with (this.iframe) {
+			width = this.inheritWidth ? this._oldWidth : "100%";
+			height = this._oldHeight;
+		}
+		this.domNode.appendChild(this.iframe);
+
+		var _iframeInitialized = false;
+
+		// now we wait for onload. Janky hack!
+		var ifrFunc = dojo.lang.hitch(this, function(){
+			if(!_iframeInitialized){
+				_iframeInitialized = true;
+			}else{ return; }
+			if(!this.editNode){
+				this.window = this.iframe.contentWindow;
+				this.document = this.iframe.contentDocument;
+			
+				// curry the getStyle function
+				var getStyle = (function (domNode) { return function (style) {
+					return dojo.style.getStyle(domNode, style);
+				}; })(this.domNode);
+				var font = getStyle('font-size') + " " + getStyle('font-family');
+		
+				var contentEditable = Boolean(document.body.contentEditable);
+				var currentDomain = (new dojo.uri.Uri(document.location)).host;
+				// alert(currentDomain);
+				with(this.document){
+					if(!contentEditable){ designMode = "on"; }
+					open();
+					write(
+						//'<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
+						'<html><title></title>\n' +
+						'<script type="text/javascript">\n' +
+						'	function init(){\n' +
+						// '		var pwidget = window.parent.dojo.widget.byId("'+this.widgetId+'");\n' +
+						// '		// pwidget.window = window\n' +
+						// '		pwidget.document = document\n' +
+						// '		alert(document.body.innerHTML);\n' +
+						// '		pwidget.onLoad();\n' +
+						// FIXME: this might not work on high ports!!!
+						'		document.domain = "'+currentDomain+'";\n' +
+						'	}\n' +
+						'</script>\n' +
+						'<style type="text/css">\n' +
+						'    body,html { padding: 0; margin: 0; font: ' + font + '; }\n' +
+						// TODO: left positioning will case contents to disappear out of view
+						//       if it gets too wide for the visible area
+						'    body { position: fixed; top: 0; left: 0; right: 0;' +
+						'        min-height: ' + this.minHeight + '; }\n' +
+						'    p { margin: 1em 0 !important; }\n' +
+						'    body > *:first-child { padding-top: 0 !important; margin-top: 0 !important; }\n' +
+						'    body > *:last-child { padding-bottom: 0 !important; margin-bottom: 0 !important; }\n' +
+						'    li > ul:-moz-first-node, li > ol:-moz-first-node { padding-top: 1.2em; }\n' +
+						'    li { min-height: 1.2em; }\n' +
+						//'    p,ul,li { padding-top: 0; padding-bottom: 0; margin-top:0; margin-bottom: 0; }\n' +
+						'</style>\n' +
+						//'<base href="' + window.location + '">' +
+						'<body' + (contentEditable ? ' contentEditable="true"' : '') + ' onload="init();">' +
+						html + '</body></html>');
+					close();
+				}
+				
+				this.onLoad();
+			}else{
+				this.editNode.innerHTML = html;
+				this.onDisplayChanged(e);
+			}
+		});
+		if(dojo.render.html.moz){
+			this.iframe.onload = ifrFunc;
+		}else{
+			ifrFunc();
+		}
+	},
+	
+	/** Draws an active x object, used by IE */
+	_drawObject: function (html) {
+		this.object = document.createElement("object");
+
+		with (this.object) {
+			classid = "clsid:2D360201-FFF5-11D1-8D03-00A0C959BC0A";
+			width = this.inheritWidth ? this._oldWidth : "100%";
+			height = this._oldHeight;
+			Scrollbars = false;
+			Appearance = this._activeX.appearance.flat;
+		}
+		this.domNode.appendChild(this.object);
+
+		this.object.attachEvent("DocumentComplete", dojo.lang.hitch(this, "onLoad"));
+		this.object.attachEvent("DisplayChanged", dojo.lang.hitch(this, "_updateHeight"));
+		this.object.attachEvent("DisplayChanged", dojo.lang.hitch(this, "onDisplayChanged"));
+
+		this.object.DocumentHTML = '<!doctype HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">' +
+			'<title></title>' +
+			'<style type="text/css">' +
+			'    body,html { padding: 0; margin: 0; }' + //font: ' + font + '; }' +
+			'    body { overflow: hidden; }' +
+			//'    #bodywrapper {  }' +
+			'</style>' +
+			//'<base href="' + window.location + '">' +
+			'<body><div id="bodywrapper">' + html + '</div></body>';
+	},
+
+/* Event handlers
+ *****************/
+
+	onLoad: function(e){
+		this.isLoaded = true;
+		if (this.object){
+			this.document = this.object.DOM;
+			this.editNode = this.document.body.firstChild;
+		}else if (this.iframe){
+			this.editNode = this.document.body;
+			this.connect(this, "onDisplayChanged", "_updateHeight");
+	
+			try { // sanity check for Mozilla
+				this.document.execCommand("useCSS", false, true); // old moz call
+				this.document.execCommand("styleWithCSS", false, false); // new moz call
+				//this.document.execCommand("insertBrOnReturn", false, false); // new moz call
+			}catch(e2){ }
+			
+			if (dojo.render.html.safari) {
+				/*
+				this.iframe.style.visiblity = "visible";
+				this.iframe.style.border = "1px solid black";
+				this.editNode.style.visiblity = "visible";
+				this.editNode.style.border = "1px solid black";
+				*/
+				// this.onDisplayChanged();
+				this.connect(this.editNode, "onblur", "onBlur");
+				this.connect(this.editNode, "onfocus", "onFocus");
+			
+				this.interval = setInterval(dojo.lang.hitch(this, "onDisplayChanged"), 750);
+				// dojo.raise("onload");
+				// dojo.debug(this.editNode.parentNode.parentNode.parentNode.nodeName);
+			} else if (dojo.render.html.mozilla) {
+
+				// We need to unhook the blur event listener on close as we
+				// can encounter a garunteed crash in FF if another event is
+				// also fired
+				var doc = this.document;
+				var blurfp = dojo.event.browser.addListener(this.document, "blur", dojo.lang.hitch(this, "onBlur"));
+				var unBlur = { unBlur: function(e){
+						dojo.event.browser.removeListener(doc, "blur", blurfp);
+				} };
+				dojo.event.connect("before", this, "close", unBlur, "unBlur");
+				dojo.event.browser.addListener(this.document, "focus", dojo.lang.hitch(this, "onFocus"));
+			
+				// safari can't handle key listeners, it kills the speed
+				var addListener = dojo.event.browser.addListener;
+				addListener(this.document, "keypress", dojo.lang.hitch(this, "onKeyPress"));
+				addListener(this.document, "keydown", dojo.lang.hitch(this, "onKeyDown"));
+				addListener(this.document, "keyup", dojo.lang.hitch(this, "onKeyUp"));
+				addListener(this.document, "click", dojo.lang.hitch(this, "onClick"));
+			}
+
+			// FIXME: when scrollbars appear/disappear this needs to be fired						
+		}
+		
+		if(this.focusOnLoad){
+			this.focus();
+		}
+		this.onDisplayChanged(e);
+	},
+
+	/** Fired on keydown */
+	onKeyDown: function (e) {
+		// we need this event at the moment to get the events from control keys
+		// such as the backspace. It might be possible to add this to Dojo, so that
+		// keyPress events can be emulated by the keyDown and keyUp detection.
+	},
+	
+	/** Fired on keyup */
+	onKeyUp: function (e) {
+	},
+	
+	/** Fired on keypress. */
+	onKeyPress: function (e) {
+		// handle the various key events
+
+		var character = e.charCode > 0 ? String.fromCharCode(e.charCode) : null;
+		var code = e.keyCode;
+				
+		var preventDefault = true; // by default assume we cancel;
+
+		// define some key combos
+		if (e.ctrlKey || e.metaKey) { // modifier pressed
+			switch (character) {
+				case "b": this.execCommand("bold"); break;
+				case "i": this.execCommand("italic"); break;
+				case "u": this.execCommand("underline"); break;
+				//case "a": this.execCommand("selectall"); break;
+				//case "k": this.execCommand("createlink", ""); break;
+				case "Z": this.execCommand("redo"); break;
+				case "s": this.close(true); break; // saves
+				default: switch (code) {
+					case e.KEY_LEFT_ARROW:
+					case e.KEY_RIGHT_ARROW:
+						//break; // preventDefault stops the browser
+						       // going through its history
+					default:
+						preventDefault = false; break; // didn't handle here
+				}
+			}
+		} else {
+			switch (code) {
+				case e.KEY_TAB:
+				  // commenting out bcs it's crashing FF
+					// this.execCommand(e.shiftKey ? "unindent" : "indent");
+					// break;
+				default:
+					preventDefault = false; break; // didn't handle here
+			}
+		}
+		
+		if (preventDefault) { e.preventDefault(); }
+
+		// function call after the character has been inserted
+		dojo.lang.setTimeout(this, this.onKeyPressed, 1, e);
+	},
+	
+	/**
+	 * Fired after a keypress event has occured and it's action taken. This
+	 * is useful if action needs to be taken after text operations have
+	 * finished
+	 */
+	onKeyPressed: function (e) {
+		// Mozilla adds a single <p> with an embedded <br> when you hit enter once:
+		//   <p><br>\n</p>
+		// when you hit enter again it adds another <br> inside your enter
+		//   <p><br>\n<br>\n</p>
+		// and if you hit enter again it splits the <br>s over 2 <p>s
+		//   <p><br>\n</p>\n<p><br>\n</p>
+		// now this assumes that <p>s have double the line-height of <br>s to work
+		// and so we need to remove the <p>s to ensure the position of the cursor
+		// changes from the users perspective when they hit enter, as the second two
+		// html snippets render the same when margins are set to 0.
+		
+		// TODO: doesn't really work; is this really needed?
+		//if (dojo.render.html.moz) {
+		//	for (var i = 0; i < this.document.getElementsByTagName("p").length; i++) {
+		//		var p = this.document.getElementsByTagName("p")[i];
+		//		if (p.innerHTML.match(/^<br>\s$/m)) {
+		//			while (p.hasChildNodes()) { p.parentNode.insertBefore(p.firstChild, p); }
+		//			p.parentNode.removeChild(p);
+		//		}
+		//	}
+		//}
+		this.onDisplayChanged(/*e*/); // can't pass in e
+	},
+	
+	onClick: function(e){ this.onDisplayChanged(e); },
+	onBlur: function(e){ },
+	_initialFocus: true,
+	onFocus: function(e){ 
+		if( (dojo.render.html.mozilla)&&(this._initialFocus) ){
+			this._initialFocus = false;
+			if(dojo.string.trim(this.editNode.innerHTML) == "&nbsp;"){
+				this.execCommand("selectall");
+				this.window.getSelection().collapseToStart();
+			}
+		}
+	},
+
+	blur: function () {
+		if (this.iframe) { this.window.blur(); }
+		else if (this.editNode) { this.editNode.blur(); }
+	},
+	
+	focus: function () {
+		if(this.iframe){
+			this.window.focus();
+		}else if(this.editNode){
+			this.editNode.focus();
+		}
+	},
+	
+	/** this event will be fired everytime the display context changes and the
+	 result needs to be reflected in the UI */
+	onDisplayChanged: function (e){ },
+	
+
+/* Formatting commands
+ **********************/
+	
+	/** IE's Active X codes */
+	_activeX: {
+		command: {
+			bold: 5000,
+			italic: 5023,
+			underline: 5048,
+
+			justifycenter: 5024,
+			justifyleft: 5025,
+			justifyright: 5026,
+
+			cut: 5003,
+			copy: 5002,
+			paste: 5032,
+			"delete": 5004,
+
+			undo: 5049,
+			redo: 5033,
+
+			removeformat: 5034,
+			selectall: 5035,
+			unlink: 5050,
+
+			indent: 5018,
+			outdent: 5031,
+
+			insertorderedlist: 5030,
+			insertunorderedlist: 5051,
+
+			// table commands
+			inserttable: 5022,
+			insertcell: 5019,
+			insertcol: 5020,
+			insertrow: 5021,
+			deletecells: 5005,
+			deletecols: 5006,
+			deleterows: 5007,
+			mergecells: 5029,
+			splitcell: 5047,
+			
+			// the command need mapping, they don't translate directly
+			// to the contentEditable commands
+			setblockformat: 5043,
+			getblockformat: 5011,
+			getblockformatnames: 5012,
+			setfontname: 5044,
+			getfontname: 5013,
+			setfontsize: 5045,
+			getfontsize: 5014,
+			setbackcolor: 5042,
+			getbackcolor: 5010,
+			setforecolor: 5046,
+			getforecolor: 5015,
+			
+			findtext: 5008,
+			font: 5009,
+			hyperlink: 5016,
+			image: 5017,
+			
+			lockelement: 5027,
+			makeabsolute: 5028,
+			sendbackward: 5036,
+			bringforward: 5037,
+			sendbelowtext: 5038,
+			bringabovetext: 5039,
+			sendtoback: 5040,
+			bringtofront: 5041,
+			
+			properties: 5052
+		},
+		
+		ui: {
+			"default": 0,
+			prompt: 1,
+			noprompt: 2
+		},
+		
+		status: {
+			notsupported: 0,
+			disabled: 1,
+			enabled: 3,
+			latched: 7,
+			ninched: 11
+		},
+		
+		appearance: {
+			flat: 0,
+			inset: 1
+		},
+		
+		state: {
+			unchecked: 0,
+			checked: 1,
+			gray: 2
+		}
+	},
+	
+	/**
+	 * Used as the advice function by dojo.event.connect to map our
+	 * normalized set of commands to those supported by the target
+	 * browser
+	 *
+	 * @param arugments The arguments Array, containing at least one
+	 *                  item, the command and an optional second item,
+	 *                  an argument.
+	 */
+	_normalizeCommand: function (joinObject){
+		var drh = dojo.render.html;
+		
+		var command = joinObject.args[0].toLowerCase();
+		if(command == "formatblock"){
+			if(drh.safari){ command = "heading"; }
+			if(drh.ie){ joinObject.args[1] = "<"+joinObject.args[1]+">"; }
+		}
+		if (command == "hilitecolor" && !drh.mozilla) { command = "backcolor"; }
+		joinObject.args[0] = command;
+		
+		if (joinObject.args.length > 1) { // a command was specified
+			var argument = joinObject.args[1];
+			if (command == "heading") { throw new Error("unimplemented"); }
+			joinObject.args[1] = argument;
+		}
+		
+		return joinObject.proceed();
+	},
+	
+	/**
+	 * Tests whether a command is supported by the host. Clients SHOULD check
+	 * whether a command is supported before attempting to use it, behaviour
+	 * for unsupported commands is undefined.
+	 *
+	 * @param command The command to test for
+	 * @return true if the command is supported, false otherwise
+	 */
+	queryCommandAvailable: function (command) {
+		var ie = 1;
+		var mozilla = 1 << 1;
+		var safari = 1 << 2;
+		var opera = 1 << 3;
+		function isSupportedBy (browsers) {
+			return {
+				ie: Boolean(browsers & ie),
+				mozilla: Boolean(browsers & mozilla),
+				safari: Boolean(browsers & safari),
+				opera: Boolean(browsers & opera)
+			}
+		}
+
+		var supportedBy = null;
+		
+		switch (command.toLowerCase()) {
+			case "bold": case "italic": case "underline":
+			case "subscript": case "superscript":
+			case "fontname": case "fontsize":
+			case "forecolor": case "hilitecolor":
+			case "justifycenter": case "justifyfull": case "justifyleft": case "justifyright":
+			case "cut": case "copy": case "paste": case "delete":
+			case "undo": case "redo":
+				supportedBy = isSupportedBy(mozilla | ie | safari | opera);
+				break;
+				
+			case "createlink": case "unlink": case "removeformat":
+			case "inserthorizontalrule": case "insertimage":
+			case "insertorderedlist": case "insertunorderedlist":
+			case "indent": case "outdent": case "formatblock": case "strikethrough": 
+				supportedBy = isSupportedBy(mozilla | ie | opera);
+				break;
+				
+			case "blockdirltr": case "blockdirrtl":
+			case "dirltr": case "dirrtl":
+			case "inlinedirltr": case "inlinedirrtl":
+				supportedBy = isSupportedBy(ie);
+				break;
+			
+			case "inserttable":
+				supportedBy = isSupportedBy(mozilla | (this.object ? ie : 0));
+				break;
+			
+			case "insertcell": case "insertcol": case "insertrow":
+			case "deletecells": case "deletecols": case "deleterows":
+			case "mergecells": case "splitcell":
+				supportedBy = isSupportedBy(this.object ? ie : 0);
+				break;
+			
+			default: return false;
+		}
+		
+		return (dojo.render.html.ie && supportedBy.ie) ||
+			(dojo.render.html.mozilla && supportedBy.mozilla) ||
+			(dojo.render.html.safari && supportedBy.safari) ||
+			(dojo.render.html.opera && supportedBy.opera);
+	},
+	
+	/**
+	 * Executes a command in the Rich Text area
+	 *
+	 * @param command The command to execute
+	 * @param argument An optional argument to the command
+	 */
+	execCommand: function (command, argument) {
+		if (this.object) {
+			if (command == "forecolor") { command = "setforecolor"; }
+			else if (command == "backcolor") { command = "setbackcolor"; }
+		
+			//if (typeof this._activeX.command[command] == "undefined") { return null; }
+		
+			if (command == "inserttable") {
+				var tableInfo = this.constructor._tableInfo;
+				if (!tableInfo) {
+					tableInfo = document.createElement("object");
+					tableInfo.classid = "clsid:47B0DFC7-B7A3-11D1-ADC5-006008A5848C";
+					document.body.appendChild(tableInfo);
+					this.constructor._table = tableInfo;
+				}
+				
+				tableInfo.NumRows = argument.rows;
+				tableInfo.NumCols = argument.cols;
+				tableInfo.TableAttrs = argument["TableAttrs"];
+				tableInfo.CellAttrs = arr["CellAttrs"];
+				tableInfo.Caption = arr["Caption"];
+			}
+		
+			if (arguments.length == 1) {
+				return this.object.ExecCommand(this._activeX.command[command],
+					this._activeX.ui.noprompt);
+			} else {
+				return this.object.ExecCommand(this._activeX.command[command],
+					this._activeX.ui.noprompt, argument);
+			}
+	
+		// fix up unlink in Mozilla to unlink the link and not just the selection
+		} else if (command == "unlink" &&
+			this.queryCommandEnabled("unlink") && dojo.render.html.mozilla) {
+			// grab selection
+			// Mozilla gets upset if we just store the range so we have to
+			// get the basic properties and recreate to save the selection
+			var selection = this.window.getSelection();
+			var selectionRange = selection.getRangeAt(0);
+			var selectionStartContainer = selectionRange.startContainer;
+			var selectionStartOffset = selectionRange.startOffset;
+			var selectionEndContainer = selectionRange.endContainer;
+			var selectionEndOffset = selectionRange.endOffset;
+			
+			// select our link and unlink
+			var range = document.createRange();
+			var a = this.getSelectedNode();
+			while (a.nodeName != "A") { a = a.parentNode; }
+			range.selectNode(a);
+			selection.removeAllRanges();
+			selection.addRange(range);
+			
+			var returnValue = this.document.execCommand("unlink", false, null);
+			
+			// restore original selection
+			var selectionRange = document.createRange();
+			selectionRange.setStart(selectionStartContainer, selectionStartOffset);
+			selectionRange.setEnd(selectionEndContainer, selectionEndOffset);
+			selection.removeAllRanges();
+			selection.addRange(selectionRange);
+			
+			return returnValue;
+		} else if (command == "inserttable" && dojo.render.html.mozilla) {
+
+			var cols = "<tr>";
+			for (var i = 0; i < argument.cols; i++) { cols += "<td></td>"; }
+			cols += "</tr>";
+		
+			var table = "<table><tbody>";
+			for (var i = 0; i < argument.rows; i++) { table += cols; }
+			table += "</tbody></table>";
+			var returnValue = this.document.execCommand("inserthtml", false, table);
+
+		} else if (command == "hilitecolor" && dojo.render.html.mozilla) {
+			// mozilla doesn't support hilitecolor properly when useCSS is
+			// set to false (bugzilla #279330)
+			
+			this.document.execCommand("useCSS", false, false);
+			var returnValue = this.document.execCommand(command, false, argument);			
+			this.document.execCommand("useCSS", false, true);
+		
+		} else {
+			argument = arguments.length > 1 ? argument : null;
+			var returnValue = this.document.execCommand(command, false, argument);
+		}
+		
+		this.onDisplayChanged();
+		return returnValue;
+	},
+
+	queryCommandEnabled: function (command, argument) {
+		if (this.object) {
+			if (command == "forecolor") { command = "setforecolor"; }
+			else if (command == "backcolor") { command = "setbackcolor"; }
+
+			if (typeof this._activeX.command[command] == "undefined") { return false; }
+			var status = this.object.QueryStatus(this._activeX.command[command]);
+			return (status != this.activeX.status.notsupported && 
+				status != this.activeX.status.diabled);
+		} else {
+			// mozilla returns true always
+			if (command == "unlink" && dojo.render.html.mozilla) {
+				var node = this.getSelectedNode();
+				while (node.parentNode && node.nodeName != "A") { node = node.parentNode; }
+				return node.nodeName == "A";
+			} else if (command == "inserttable" && dojo.render.html.mozilla) {
+				return true;
+			}
+			return this.document.queryCommandEnabled(command);
+		}
+	},
+
+	queryCommandState: function (command, argument) {
+		if (this.object) {
+			if (command == "forecolor") { command = "setforecolor"; }
+			else if (command == "backcolor") { command = "setbackcolor"; }
+
+			if (typeof this._activeX.command[command] == "undefined") { return null; }
+			var status = this.object.QueryStatus(this._activeX.command[command]);
+			return (status == this._activeX.status.enabled ||
+				status == this._activeX.status.ninched);
+		} else {
+			return this.document.queryCommandState(command);
+		}
+	},
+
+	queryCommandValue: function (command, argument) {
+		if (this.object) {
+			switch (command) {
+				case "forecolor":
+				case "backcolor":
+				case "fontsize":
+				case "fontname":
+				case "blockformat":
+					command = "get" + command;
+					return this.object.execCommand(
+						this._activeX.command[command],
+						this._activeX.ui.noprompt);
+			}			
+		
+			//var status = this.object.QueryStatus(this._activeX.command[command]);
+		} else {
+			return this.document.queryCommandValue(command);
+		}
+	},
+	
+	
+/* Misc.
+ ********/
+
+	getSelectedNode: function () {
+		if(!this.isLoaded){ return; }
+		if (this.document.selection) {
+			return this.document.selection.createRange().parentElement();
+		} else if (dojo.render.html.mozilla) {
+			return this.window.getSelection().getRangeAt(0).commonAncestorContainer;
+		}
+		return this.editNode;
+	},
+	
+	placeCursorAtStart: function () {
+		if(!this.isLoaded){
+			dojo.event.connect(this, "onLoad", this, "placeCursorAtEnd");
+			return;
+		}
+		dojo.event.disconnect(this, "onLoad", this, "placeCursorAtEnd");
+		if (this.window.getSelection) {
+			var selection = this.window.getSelection;
+			if (selection.removeAllRanges) { // Mozilla			
+				var range = this.document.createRange();
+				range.selectNode(this.editNode.firstChild);
+				range.collapse(true);
+				var selection = this.window.getSelection();
+				selection.removeAllRanges();
+				selection.addRange(range);
+			} else { // Safari
+				// not a great deal we can do
+			}
+		} else if (this.document.selection) { // IE
+			var range = this.document.body.createTextRange();
+			range.moveToElementText(this.editNode);
+			range.collapse(true);
+			range.select();
+		}
+	},
+	
+	placeCursorAtEnd: function () {
+		if(!this.isLoaded){
+			dojo.event.connect(this, "onLoad", this, "placeCursorAtEnd");
+			return;
+		}
+		dojo.event.disconnect(this, "onLoad", this, "placeCursorAtEnd");
+		if (this.window.getSelection) {
+			var selection = this.window.getSelection;
+			if (selection.removeAllRanges) { // Mozilla			
+				var range = this.document.createRange();
+				range.selectNode(this.editNode.lastChild);
+				range.collapse(false);
+				var selection = this.window.getSelection();
+				selection.removeAllRanges();
+				selection.addRange(range);
+			} else { // Safari
+				// not a great deal we can do
+			}
+		} else if (this.document.selection) { // IE
+			var range = this.document.body.createTextRange();
+			range.moveToElementText(this.editNode);
+			range.collapse(true);
+			range.select();
+		}
+	},
+
+	_lastHeight: 0,
+
+	/** Updates the height of the iframe to fit the contents. */
+	_updateHeight: function () {
+		if (this.iframe) {
+			/*
+			if(!this.document.body["offsetHeight"]){
+				return;
+			}
+			*/
+			// The height includes the padding, borders and margins so these
+			// need to be added on
+			var heights = ["margin-top", "margin-bottom",
+				"padding-bottom", "padding-top",
+				"border-width-bottom", "border-width-top"];
+			for (var i = 0, chromeheight = 0; i < heights.length; i++) {
+				var height = dojo.style.getStyle(this.iframe, heights[i]);
+				// Safari doesn't have all the heights so we have to test
+				if (height) {
+					chromeheight += Number(height.replace(/[^0-9]/g, ""));
+				}
+			}
+			// dojo.debug(this.document.body.offsetHeight);
+			// dojo.debug(chromeheight);
+			if(this.document.body["offsetHeight"]){
+				this._lastHeight = this.document.body.offsetHeight + chromeheight;
+				this.iframe.height = this._lastHeight + "px";
+				this.window.scrollTo(0, 0);
+			}
+			// this.iframe.height = this._lastHeight + "px";
+			// dojo.debug(this.iframe.height);
+		} else if (this.object) {
+			this.object.height = dojo.style.getInnerHeight(this.editNode);
+		}
+	},
+	
+	/**
+	 * Saves the content in an onunload event if the editor has not been closed
+	 */
+	_saveContent: function(e){
+		var saveTextarea = document.getElementById("dojo.widget.RichText.savedContent");
+		saveTextarea.value += this._SEPARATOR + this.saveName + ":" + this.getEditorContent();
+	},
+
+	getEditorContent: function(){
+		var ec = "";
+		try{
+			ec = (this._content.length > 0) ? this._content : this.editNode.innerHTML;
+			if(dojo.string.trim(ec) == "&nbsp;"){ ec = ""; }
+		}catch(e){ /* squelch */ }
+
+		dojo.lang.forEach(this.contentFilters, function(ef){
+			ec = ef(ec);
+		});
+		return ec;
+	},
+	
+	/**
+	 * Kills the editor and optionally writes back the modified contents to the 
+	 * element from which it originated.
+	 *
+	 * @param save Whether or not to save the changes. If false, the changes are
+	 *             discarded.
+	 * @return true if the contents has been modified, false otherwise
+	 */
+	close: function(save, force){
+		if(this.isClosed){return false; }
+
+		if (arguments.length == 0) { save = true; }
+		this._content = this.editNode.innerHTML;
+		var changed = (this.savedContent.innerHTML != this._content);
+		
+		// line height is squashed for iframes
+		if (this.iframe){ this.domNode.style.lineHeight = null; }
+		
+		if(this.interval){ clearInterval(this.interval); }
+		
+		if(dojo.render.html.ie && !this.object){
+			dojo.event.browser.clean(this.editNode);
+		}
+		if(dojo.render.html.moz){
+			var ifr = this.domNode.firstChild;
+			ifr.style.display = "none";
+			/*
+			setTimeout(function(){
+				ifr.parentNode.removeChild(ifr);
+			}, 0);
+			*/
+		}else{
+			this.domNode.innerHTML = "";
+		}
+		// dojo.dom.removeChildren(this.domNode);
+		if(save){
+			// kill listeners on the saved content
+			dojo.event.browser.clean(this.savedContent);
+			if(dojo.render.html.moz){
+				var nc = document.createElement("span");
+				this.domNode.appendChild(nc);
+				nc.innerHTML = this.editNode.innerHTML;
+			}else{
+				this.domNode.innerHTML = this._content;
+			}
+		} else {
+			while (this.savedContent.hasChildNodes()) {
+				this.domNode.appendChild(this.savedContent.firstChild);
+			}
+		}
+		delete this.savedContent;
+		
+		dojo.html.removeClass(this.domNode, "RichTextEditable");
+		this.isClosed = true;
+		this.isLoaded = false;
+
+		return changed;
+	},
+	
+	destroy: function () {
+		if (!this.isClosed) { this.close(false); }
+	
+		// disconnect those listeners.
+		while (this._connected.length) {
+			this.disconnect(this._connected[0],
+				this._connected[1], this._connected[2]);
+		}
+	},
+
+	_connected: [],
+	connect: function (targetObj, targetFunc, thisFunc) {
+		dojo.event.connect(targetObj, targetFunc, this, thisFunc);
+		// this._connected.push([targetObj, targetFunc, thisFunc]);	
+	},
+	
+	// FIXME: below two functions do not work with the above line commented out
+	disconnect: function (targetObj, targetFunc, thisFunc) {
+		for (var i = 0; i < this._connected.length; i++) {
+			if (this._connected[0] == targetObj &&
+				this._connected[1] == targetFunc &&
+				this._connected[2] == thisFunc) {
+				dojo.event.disconnect(targetObj, targetFunc, this, thisFunc);
+				this._connected.splice(i, 1);
+				break;
+			}
+		}
+	},
+	
+	disconnectAllWithRoot: function (targetObj) {
+		for (var i = 0; i < this._connected.length; i++) {
+			if (this._connected[0] == targetObj) {
+				dojo.event.disconnect(targetObj,
+					this._connected[1], this, this._connected[2]);
+				this._connected.splice(i, 1);
+			}
+		}	
+	}
+	
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SimpleDropdownButtons.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SimpleDropdownButtons.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SimpleDropdownButtons.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SimpleDropdownButtons.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,158 @@
+/*
+	Copyright (c) 2004-2005, 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
+*/
+
+/* TODO:
+ * - make the dropdowns "smart" so they can't get cutoff on bottom of page, sides of page, etc.
+ * - unify menus with the MenuItem and Menu classes so we can add stuff to all menus at once
+ * - allow buttons to be enabled/disabled at runtime
+ *     - this probably means creating all menus upfront and then triggering a disable action
+ *       for disabled buttons in the constructor loop. we'll need a disable and enable action anyway
+ * - should each button with menu be a widget object of it's own?
+ */
+dojo.provide("dojo.widget.SimpleDropdownButtons");
+dojo.provide("dojo.widget.HtmlSimpleDropdownButtons");
+
+dojo.deprecated("dojo.widget.SimpleDropdownButtons",  "use dojo.widget.DropDownButton2", "0.4");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.dom");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:simpledropdownbuttons");
+
+dojo.widget.HtmlSimpleDropdownButtons = function() {
+	dojo.widget.HtmlWidget.call(this);
+
+	this.widgetType = "SimpleDropdownButtons";
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlSimpleDropdownButtons.css");
+
+	this.menuTriggerClass = "dojoSimpleDropdownButtons";
+	this.menuClass = "dojoSimpleDropdownButtonsMenu";
+
+	// overwrite buildRendering so we don't clobber our list
+	this.buildRendering = function(args, frag) {
+		if(this.templateCssPath) {
+			dojo.style.insertCssFile(this.templateCssPath, null, true);
+		}
+		this.domNode = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+
+		var menu = this.domNode;
+		if( !dojo.html.hasClass(menu, this.menuTriggerClass) ) {
+			dojo.html.addClass(menu, this.menuTriggerClass);
+		}
+		var li = dojo.dom.getFirstChildElement(menu);
+		var menuIDs = [];
+		var arrowIDs = [];
+
+		while(li) {
+			if(li.getElementsByTagName("ul").length > 0) {
+				var a = dojo.dom.getFirstChildElement(li);
+				var arrow = document.createElement("a");
+				arrow.href = "javascript:;";
+				arrow.innerHTML = "&nbsp;";
+				dojo.html.setClass(arrow, "downArrow");
+				if(!arrow.id) {
+					arrow.id = dojo.dom.getUniqueId();
+				}
+				arrowIDs.push(arrow.id);
+				var submenu = dojo.dom.getNextSiblingElement(a);
+				if(!submenu.id) {
+					submenu.id = dojo.dom.getUniqueId();
+				}
+				menuIDs.push(submenu.id);
+
+				if( dojo.html.hasClass(a, "disabled") ) {
+					dojo.html.addClass(arrow, "disabled");
+					dojo.html.disableSelection(li);
+					arrow.onfocus = function(){ this.blur(); }
+				} else {
+					dojo.html.addClass(submenu, this.menuClass);
+					document.body.appendChild(submenu);
+					dojo.event.connect(arrow, "onmousedown", (function() {
+						var ar = arrow;
+						return function(e) {
+							dojo.html.addClass(ar, "pressed");
+						}
+					})());
+					dojo.event.connect(arrow, "onclick", (function() {
+						var aa = a;
+						var ar = arrow;
+						var sm = submenu;
+						var setWidth = false;
+
+						return function(e) {
+							hideAll(sm, ar);
+							sm.style.left = (dojo.html.getScrollLeft()
+								+ e.clientX - e.layerX + aa.offsetLeft) + "px";
+							sm.style.top = (dojo.html.getScrollTop() + e.clientY
+								- e.layerY + aa.offsetTop + aa.offsetHeight) + "px";
+							sm.style.display = sm.style.display == "block" ? "none" : "block";
+							if(sm.style.display == "none") {
+								dojo.html.removeClass(ar, "pressed");
+								e.target.blur()
+							}
+							if(!setWidth && sm.style.display == "block"
+								&& sm.offsetWidth < aa.offsetWidth + ar.offsetWidth) {
+								sm.style.width = aa.offsetWidth + ar.offsetWidth + "px";
+								setWidth = true;
+							}
+							e.preventDefault();
+						}
+					})());
+				}
+
+				dojo.event.connect(a, "onclick", function(e) {
+					if(e && e.target && e.target.blur) {
+						e.target.blur();
+					}
+				});
+
+				if(a.nextSibling) {
+					li.insertBefore(arrow, a.nextSibling);
+				} else {
+					li.appendChild(arrow);
+				}
+
+			}
+			li = dojo.dom.getNextSiblingElement(li);
+		}
+
+		function hideAll(excludeMenu, excludeArrow) {
+			// hide menus
+			for(var i = 0; i < menuIDs.length; i++) {
+				var m = document.getElementById(menuIDs[i]);
+				if(!excludeMenu || m != excludeMenu) {
+					document.getElementById(menuIDs[i]).style.display = "none";
+				}
+			}
+			// restore arrows to non-pressed state
+			for(var i = 0; i < arrowIDs.length; i++) {
+				var m = document.getElementById(arrowIDs[i]);
+				if(!excludeArrow || m != excludeArrow) {
+					dojo.html.removeClass(m, "pressed");
+				}
+			}
+		}
+
+		dojo.event.connect(document.documentElement, "onmousedown", function(e) {
+			if( dojo.html.hasClass(e.target, "downArrow") ) { return };
+			for(var i = 0; i < menuIDs.length; i++) {
+				if( dojo.dom.isDescendantOf(e.target, document.getElementById(menuIDs[i])) ) {
+					return;
+				}
+			}
+			hideAll();
+		});
+	}
+}
+dojo.inherits(dojo.widget.HtmlSimpleDropdownButtons, dojo.widget.HtmlWidget);

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SlideShow.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SlideShow.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SlideShow.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SlideShow.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,133 @@
+/*
+	Copyright (c) 2004-2005, 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.SlideShow");
+dojo.provide("dojo.widget.html.SlideShow");
+
+dojo.require("dojo.event");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.fx.html");
+dojo.require("dojo.style");
+
+dojo.widget.html.SlideShow = function(){
+	dojo.widget.HtmlWidget.call(this);
+
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlSlideShow.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlSlideShow.css");
+
+	// over-ride some defaults
+	this.isContainer = false;
+	this.widgetType = "SlideShow";
+
+	// useful properties
+	this.imgUrls = [];		// the images we'll go through
+	this.imgUrlBase = "";
+	this.urlsIdx = 0;		// where in the images we are
+	this.delay = 4000; 		// give it 4 seconds
+	this.transitionInterval = 2000; // 2 seconds
+	this.imgWidth = 800;	// img width
+	this.imgHeight = 600;	// img height
+	this.background = "img2"; // what's in the bg
+	this.foreground = "img1"; // what's in the fg
+	this.stopped = false;	// should I stay or should I go?
+	this.fadeAnim = null; // references our animation
+
+	// our DOM nodes:
+	this.imagesContainer = null;
+	this.startStopButton = null;
+	this.controlsContainer = null;
+	this.img1 = null;
+	this.img2 = null;
+
+	this.fillInTemplate = function(){
+		dojo.style.setOpacity(this.img1, 0.9999);
+		dojo.style.setOpacity(this.img2, 0.9999);
+		with(this.imagesContainer.style){
+			width = this.imgWidth+"px";
+			height = this.imgHeight+"px";
+		}
+		with(this.img1.style){
+			width = this.imgWidth+"px";
+			height = this.imgHeight+"px";
+		}
+		with(this.img2.style){
+			width = this.imgWidth+"px";
+			height = this.imgHeight+"px";
+		}
+		if(this.imgUrls.length>1){
+			this.img2.src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+			this.endTransition();
+		}else{
+			this.img1.src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+		}
+	}
+
+	this.togglePaused = function(){
+		if(this.stopped){
+			this.stopped = false;
+			this.endTransition();
+			this.startStopButton.value= "pause";
+		}else{
+			this.stopped = true;
+			this.startStopButton.value= "play";
+		}
+	}
+
+	this.backgroundImageLoaded = function(){
+		// start fading out the foreground image
+		if(this.stopped){ return; }
+		// closure magic for callback
+		var _this = this; 
+		var callback = function(){ _this.endTransition(); };
+
+		// actually start the fadeOut effect
+		// NOTE: if we wanted to use other transition types, we'd set them up
+		// 		 here as well
+		if(this.fadeAnim) {
+			this.fadeAnim.stop();
+		}
+		this.fadeAnim = dojo.fx.html.fadeOut(this[this.foreground], 
+			this.transitionInterval, callback);
+	}
+
+	this.endTransition = function(){
+		// move the foreground image to the background 
+		with(this[this.background].style){ zIndex = parseInt(zIndex)+1; }
+		with(this[this.foreground].style){ zIndex = parseInt(zIndex)-1; }
+
+		// fg/bg book-keeping
+		var tmp = this.foreground;
+		this.foreground = this.background;
+		this.background = tmp;
+
+		// keep on truckin
+		this.loadNextImage();
+	}
+
+	this.loadNextImage = function(){
+		// load a new image in that container, and make sure it informs
+		// us when it finishes loading
+		dojo.event.kwConnect({
+			srcObj: this[this.background],
+			srcFunc: "onload",
+			adviceObj: this,
+			adviceFunc: "backgroundImageLoaded",
+			once: true, // make sure we only ever hear about it once
+			delay: this.delay
+		});
+		dojo.style.setOpacity(this[this.background], 1.0);
+		this[this.background].src = this.imgUrlBase+this.imgUrls[this.urlsIdx++];
+		if(this.urlsIdx>(this.imgUrls.length-1)){
+			this.urlsIdx = 0;
+		}
+	}
+}
+dojo.inherits(dojo.widget.html.SlideShow, dojo.widget.HtmlWidget);
+dojo.widget.tags.addParseTreeHandler("dojo:slideshow");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SortableTable.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SortableTable.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SortableTable.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SortableTable.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,38 @@
+/*
+	Copyright (c) 2004-2005, 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.SortableTable");
+dojo.require("dojo.widget.*");
+dojo.requireAfterIf("html", "dojo.widget.html.SortableTable");
+dojo.widget.tags.addParseTreeHandler("dojo:sortableTable");
+
+//	set up the general widget
+dojo.widget.SortableTable=function(){
+	//	summary
+	//	base class for the SortableTable
+	dojo.widget.Widget.call(this);
+	this.widgetType="SortableTable";
+	this.isContainer=false;
+
+	//	custom properties
+	this.enableMultipleSelect=false;
+	this.maximumNumberOfSelections=0;	//	0 for unlimited, is the default.
+	this.enableAlternateRows=false;
+	this.minRows=0;	//	0 means ignore.
+	this.defaultDateFormat="%D";
+	this.data=[];
+	this.selected=[];		//	always an array to handle multiple selections.
+	this.columns=[];
+	this.sortIndex=0;		//	index of the column sorted on, first is the default.
+	this.sortDirection=0;	//	0==asc, 1==desc
+	this.valueField="Id";	//	if a JSON structure is parsed and there is a field of this name,
+							//	a value attribute will be added to the row (tr value="{Id}")
+};
+dojo.inherits(dojo.widget.SortableTable, dojo.widget.Widget);

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SplitPane.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SplitPane.js?rev=385171&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SplitPane.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SplitPane.js Sat Mar 11 13:18:41 2006
@@ -0,0 +1,549 @@
+/*
+	Copyright (c) 2004-2005, 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.SplitPane");
+dojo.provide("dojo.widget.SplitPanePanel");
+dojo.provide("dojo.widget.html.SplitPane");
+dojo.provide("dojo.widget.html.SplitPanePanel");
+
+//
+// TODO
+// make it prettier
+// active dragging upwards doesn't always shift other bars (direction calculation is wrong in this case)
+//
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutPane");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+
+dojo.widget.html.SplitPane = function(){
+
+	dojo.widget.HtmlWidget.call(this);
+
+	this.sizers = [];
+}
+
+dojo.inherits(dojo.widget.html.SplitPane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.SplitPane, {
+	widgetType: "SplitPane",
+	isContainer: true,
+
+	virtualSizer: null,
+	isHorizontal: 0,
+	paneBefore: null,
+	paneAfter: null,
+	isSizing: false,
+	dragOffset: null,
+	startPoint: null,
+	lastPoint: null,
+	sizingSplitter: null,
+	isActiveResize: 0,
+	offsetX: 0,
+	offsetY: 0,
+	isDraggingLeft: 0,
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlSplitPane.css"),
+	originPos: null,
+
+	activeSizing: '',
+	sizerWidth: 15,
+	orientation: 'horizontal',
+
+	debugName: '',
+
+	fillInTemplate: function(){
+
+		dojo.style.insertCssFile(this.templateCssPath, null, true);
+		dojo.html.addClass(this.domNode, "dojoHtmlSplitPane");
+		this.domNode.style.overflow='hidden';	// workaround firefox bug
+
+		this.paneWidth = dojo.style.getContentWidth(this.domNode);
+		this.paneHeight = dojo.style.getContentHeight(this.domNode);
+
+		this.isHorizontal = (this.orientation == 'horizontal') ? 1 : 0;
+		this.isActiveResize = (this.activeSizing == '1') ? 1 : 0;
+
+		//dojo.debug("fillInTemplate for "+this.debugName);
+	},
+
+	onResized: function(e){
+		this.paneWidth = dojo.style.getContentWidth(this.domNode);
+		this.paneHeight = dojo.style.getContentHeight(this.domNode);
+		this.layoutPanels();
+		this.notifyChildrenOfResize();	// notify children they've been moved/resized
+	},
+
+	postCreate: function(args, fragment, parentComp){
+
+		// dojo.debug("post create for "+this.debugName);
+
+		// attach the children and create the draggers
+		for(var i=0; i<this.children.length; i++){
+            with(this.children[i].domNode.style){
+                position = "absolute";
+            }
+            dojo.html.addClass(this.children[i].domNode,
+                "dojoHtmlSplitterPanePanel");
+
+            if(i == this.children.length-1){
+                break;
+            }
+
+            this._addSizer();
+		}
+
+		// create the fake dragger
+		this.virtualSizer = document.createElement('div');
+		this.virtualSizer.style.position = 'absolute';
+		this.virtualSizer.style.display = 'none';
+		//this.virtualSizer.style.backgroundColor = 'lime';
+		this.virtualSizer.style.zIndex = 10;
+		this.virtualSizer.className = this.isHorizontal ? 'dojoHtmlSplitPaneVirtualSizerH' : 'dojoHtmlSplitPaneVirtualSizerV';
+		this.domNode.appendChild(this.virtualSizer);
+
+		dojo.html.disableSelection(this.virtualSizer);
+
+		// size the panels once the browser has caught up
+		this.resizeSoon();
+	},
+
+    _injectChild: function(child) {
+        with(child.domNode.style){
+            position = "absolute";
+        }
+        dojo.html.addClass(child.domNode,
+            "dojoHtmlSplitterPanePanel");
+    },
+
+    _addSizer: function() {
+        var i = this.sizers.length;
+
+        this.sizers[i] = document.createElement('div');
+        this.sizers[i].style.position = 'absolute';
+        this.sizers[i].className = this.isHorizontal ? 'dojoHtmlSplitPaneSizerH' : 'dojoHtmlSplitPaneSizerV';
+
+        var self = this;
+        var handler = (function(){ var sizer_i = i; return function(e){ self.beginSizing(e, sizer_i); } })();
+        dojo.event.connect(this.sizers[i], "onmousedown", handler);
+
+        this.domNode.appendChild(this.sizers[i]);
+        dojo.html.disableSelection(this.sizers[i]);
+    },
+
+    removeChild: function(widget){
+        // Remove sizer, but only if widget is really our child and
+        // we have at least one sizer to throw away
+        if (this.sizers.length > 0) {
+            for(var x=0; x<this.children.length; x++){
+                if(this.children[x] === widget){
+                    var i = this.sizers.length - 1;
+                    this.domNode.removeChild(this.sizers[i]);
+                    this.sizers.length = i;
+                    break;
+                }
+            }
+        }
+
+        // Remove widget and repaint
+        dojo.widget.html.SplitPane.superclass.removeChild.call(this, widget, arguments);
+        this.onResized();
+    },
+
+    addChild: function(widget, overrideContainerNode, pos, ref, insertIndex){
+        dojo.widget.html.SplitPane.superclass.addChild.call(this, widget, overrideContainerNode, pos, ref, insertIndex);
+        this._injectChild(widget);
+
+        if (this.children.length > 1) {
+            this._addSizer();
+        }
+
+        this.layoutPanels();
+    },
+
+    layoutPanels: function(){
+        if (this.children.length == 0){ return; }
+
+		//
+		// calculate space
+		//
+
+		var space = this.isHorizontal ? this.paneWidth : this.paneHeight;
+
+		if (this.children.length > 1){
+
+			space -= this.sizerWidth * (this.children.length - 1);
+		}
+
+
+		//
+		// calculate total of SizeShare values
+		//
+
+		var out_of = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			out_of += this.children[i].sizeShare;
+		}
+
+
+		//
+		// work out actual pixels per sizeshare unit
+		//
+
+		var pix_per_unit = space / out_of;
+
+
+		//
+		// set the SizeActual member of each pane
+		//
+
+		var total_size = 0;
+
+		for(var i=0; i<this.children.length-1; i++){
+
+			var size = Math.round(pix_per_unit * this.children[i].sizeShare);
+			this.children[i].sizeActual = size;
+			total_size += size;
+		}
+		this.children[this.children.length-1].sizeActual = space - total_size;
+
+		//
+		// make sure the sizes are ok
+		//
+
+		this.checkSizes();
+
+
+		//
+		// now loop, positioning each pane and letting children resize themselves
+		//
+
+		var pos = 0;
+		var size = this.children[0].sizeActual;
+		this.movePanel(this.children[0].domNode, pos, size);
+		this.children[0].position = pos;
+        this.children[0].onResized();
+		pos += size;
+
+		for(var i=1; i<this.children.length; i++){
+
+			// first we position the sizing handle before this pane
+			this.movePanel(this.sizers[i-1], pos, this.sizerWidth);
+			this.sizers[i-1].position = pos;
+			pos += this.sizerWidth;
+
+			size = this.children[i].sizeActual;
+			this.movePanel(this.children[i].domNode, pos, size);
+			this.children[i].position = pos;
+            this.children[i].onResized();
+			pos += size;
+		}
+	},
+
+	movePanel: function(panel, pos, size){
+		if (this.isHorizontal){
+			panel.style.left = pos + 'px';
+			panel.style.top = 0;
+
+			dojo.style.setOuterWidth(panel, size);
+			dojo.style.setOuterHeight(panel, this.paneHeight);
+		}else{
+			panel.style.left = 0;
+			panel.style.top = pos + 'px';
+
+			dojo.style.setOuterWidth(panel, this.paneWidth);
+			dojo.style.setOuterHeight(panel, size);
+		}
+	},
+
+	growPane: function(growth, pane){
+
+		if (growth > 0){
+			if (pane.sizeActual > pane.sizeMin){
+				if ((pane.sizeActual - pane.sizeMin) > growth){
+
+					// stick all the growth in this pane
+					pane.sizeActual = pane.sizeActual - growth;
+					growth = 0;
+				}else{
+					// put as much growth in here as we can
+					growth -= pane.sizeActual - pane.sizeMin;
+					pane.sizeActual = pane.sizeMin;
+				}
+			}
+		}
+		return growth;
+	},
+
+	checkSizes: function(){
+
+		var total_min_size = 0;
+		var total_size = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			total_size += this.children[i].sizeActual;
+			total_min_size += this.children[i].sizeMin;
+		}
+
+		// only make adjustments if we have enough space for all the minimums
+
+		if (total_min_size <= total_size){
+
+			var growth = 0;
+
+			for(var i=0; i<this.children.length; i++){
+
+				if (this.children[i].sizeActual < this.children[i].sizeMin){
+
+					growth += this.children[i].sizeMin - this.children[i].sizeActual;
+					this.children[i].sizeActual = this.children[i].sizeMin;
+				}
+			}
+
+			if (growth > 0){
+				if (this.isDraggingLeft){
+					for(var i=this.children.length-1; i>=0; i--){
+						growth = this.growPane(growth, this.children[i]);
+					}
+				}else{
+					for(var i=0; i<this.children.length; i++){
+						growth = this.growPane(growth, this.children[i]);
+					}
+				}
+			}
+		}else{
+
+			for(var i=0; i<this.children.length; i++){
+				this.children[i].sizeActual = Math.round(total_size * (this.children[i].sizeMin / total_min_size));
+			}
+		}
+	},
+
+	beginSizing: function(e, i){
+		var clientX = window.event ? window.event.offsetX : e.layerX;
+		var clientY = window.event ? window.event.offsetY : e.layerY;
+		var screenX = window.event ? window.event.clientX : e.pageX;
+		var screenY = window.event ? window.event.clientY : e.pageY;
+
+		this.paneBefore = this.children[i];
+		this.paneAfter = this.children[i+1];
+
+		this.isSizing = true;
+		this.sizingSplitter = this.sizers[i];
+		this.originPos = dojo.style.getAbsolutePosition(this.domNode, true);
+		this.dragOffset = {'x':clientX, 'y':clientY};
+		this.startPoint  = {'x':screenX, 'y':screenY};
+		this.lastPoint  = {'x':screenX, 'y':screenY};
+
+		this.offsetX = screenX - clientX;
+		this.offsetY = screenY - clientY;
+
+		if (!this.isActiveResize){
+			this.showSizingLine();
+		}
+
+		//
+		// attach mouse events
+		//
+
+		dojo.event.connect(document.documentElement, "onmousemove", this, "changeSizing");
+		dojo.event.connect(document.documentElement, "onmouseup", this, "endSizing");
+	},
+
+	changeSizing: function(e){
+
+		// FIXME: is this fixed in connect()?
+		var screenX = window.event ? window.event.clientX : e.pageX;
+		var screenY = window.event ? window.event.clientY : e.pageY;
+
+		if (this.isActiveResize){
+			this.lastPoint = {'x':screenX, 'y':screenY};
+			this.movePoint();
+			this.updateSize();
+		}else{
+			this.lastPoint = {'x':screenX, 'y':screenY};
+			this.movePoint();
+			this.moveSizingLine();
+		}
+	},
+
+	endSizing: function(e){
+
+		if (!this.isActiveResize){
+			this.hideSizingLine();
+		}
+
+		this.updateSize();
+
+		this.isSizing = false;
+
+		dojo.event.disconnect(document.documentElement, "onmousemove", this, "changeSizing");
+		dojo.event.disconnect(document.documentElement, "onmouseup", this, "endSizing");
+	},
+
+	movePoint: function(){
+
+		// make sure FLastPoint is a legal point to drag to
+		p = this.screenToMainClient(this.lastPoint);
+
+		if (this.isHorizontal){
+
+			var a = p.x - this.dragOffset.x;
+			a = this.legaliseSplitPoint(a);
+			p.x = a + this.dragOffset.x;
+		}else{
+			var a = p.y - this.dragOffset.y;
+			a = this.legaliseSplitPoint(a);
+			p.y = a + this.dragOffset.y;
+		}
+
+		this.lastPoint = this.mainClientToScreen(p);
+	},
+
+	screenToClient: function(pt){
+
+		pt.x -= (this.offsetX + this.sizingSplitter.position);
+		pt.y -= (this.offsetY + this.sizingSplitter.position);
+
+		return pt;
+	},
+
+	clientToScreen: function(pt){
+
+		pt.x += (this.offsetX + this.sizingSplitter.position);
+		pt.y += (this.offsetY + this.sizingSplitter.position);
+
+		return pt;
+	},
+
+	screenToMainClient: function(pt){
+
+		pt.x -= this.offsetX;
+		pt.y -= this.offsetY;
+
+		return pt;
+	},
+
+	mainClientToScreen: function(pt){
+
+		pt.x += this.offsetX;
+		pt.y += this.offsetY;
+
+		return pt;
+	},
+
+	legaliseSplitPoint: function(a){
+
+		a += this.sizingSplitter.position;
+
+		this.isDraggingLeft = (a > 0) ? 1 : 0;
+
+		if (!this.isActiveResize){
+
+			if (a < this.paneBefore.position + this.paneBefore.sizeMin){
+
+				a = this.paneBefore.position + this.paneBefore.sizeMin;
+			}
+
+			if (a > this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin))){
+
+				a = this.paneAfter.position + (this.paneAfter.sizeActual - (this.sizerWidth + this.paneAfter.sizeMin));
+			}
+		}
+
+		a -= this.sizingSplitter.position;
+
+		this.checkSizes();
+
+		return a;
+	},
+
+	updateSize: function(){
+
+		var p = this.clientToScreen(this.lastPoint);
+		var p = this.screenToClient(this.lastPoint);
+
+		var pos = this.isHorizontal ? p.x - (this.dragOffset.x + this.originPos.x) : p.y - (this.dragOffset.y + this.originPos.y);
+
+		var start_region = this.paneBefore.position;
+		var end_region   = this.paneAfter.position + this.paneAfter.sizeActual;
+
+		this.paneBefore.sizeActual = pos - start_region;
+		this.paneAfter.position    = pos + this.sizerWidth;
+		this.paneAfter.sizeActual  = end_region - this.paneAfter.position;
+
+		for(var i=0; i<this.children.length; i++){
+
+			this.children[i].sizeShare = this.children[i].sizeActual;
+		}
+
+		this.layoutPanels();
+	},
+
+	showSizingLine: function(){
+
+		this.moveSizingLine();
+
+		if (this.isHorizontal){
+			dojo.style.setOuterWidth(this.virtualSizer, this.sizerWidth);
+			dojo.style.setOuterHeight(this.virtualSizer, this.paneHeight);
+		}else{
+			dojo.style.setOuterWidth(this.virtualSizer, this.paneWidth);
+			dojo.style.setOuterHeight(this.virtualSizer, this.sizerWidth);
+		}
+
+		this.virtualSizer.style.display = 'block';
+	},
+
+	hideSizingLine: function(){
+
+		this.virtualSizer.style.display = 'none';
+	},
+
+	moveSizingLine: function(){
+
+		var origin = {'x':0, 'y':0};
+
+		if (this.isHorizontal){
+			origin.x += (this.lastPoint.x - this.startPoint.x) + this.sizingSplitter.position;
+		}else{
+			origin.y += (this.lastPoint.y - this.startPoint.y) + this.sizingSplitter.position;
+		}
+
+		this.virtualSizer.style.left = origin.x + 'px';
+		this.virtualSizer.style.top = origin.y + 'px';
+	}
+});
+
+// These arguments can be specified for the children of a SplitPane.
+// Since any widget can be specified as a SplitPane child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	sizeMin: 10,
+	sizeShare: 10
+});
+
+// Deprecated class for split pane children.
+// Actually any widget can be the child of a split pane
+dojo.widget.html.SplitPanePanel = function(){
+	dojo.widget.html.LayoutPane.call(this);
+}
+dojo.inherits(dojo.widget.html.SplitPanePanel, dojo.widget.html.LayoutPane);
+dojo.lang.extend(dojo.widget.html.SplitPanePanel, {
+	widgetType: "SplitPanePanel"
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:SplitPane");
+dojo.widget.tags.addParseTreeHandler("dojo:SplitPanePanel");



---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org