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/02/01 06:41:23 UTC

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

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=373998&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 Tue Jan 31 21:39:49 2006
@@ -0,0 +1,1042 @@
+/*
+	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");
+
+// 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 = this.textarea.value;
+			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 = this.domNode.innerHTML;
+		}
+				
+		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);
+				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' +
+						'	}\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){ },
+	onFocus: function (e){ },
+
+	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;
+		}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){
+			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;
+			}
+			// kill listeners on the saved content
+			dojo.event.browser.clean(this.savedContent);
+		} 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=373998&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 Tue Jan 31 21:39:49 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=373998&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 Tue Jan 31 21:39:49 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=373998&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 Tue Jan 31 21:39:49 2006
@@ -0,0 +1,35 @@
+/*
+	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(){
+	dojo.widget.Widget.call(this);
+	this.widgetType="SortableTable";
+	this.isContainer=false;
+
+	//	custom properties
+	this.enableMultipleSelect=false;
+	this.maximumNumberOfSelections=1;
+	this.enableAlternateRows=false;
+	this.minRows=0;	//	0 means ignore.
+	this.defaultDateFormat="#M/#d/#yyyy";
+	this.data=[];
+	this.selected=null
+	this.columns=[];
+	this.sortIndex=0;		//	index of the column sorted on, first is the default.
+	this.sortDirection=0;	//	0==asc, 1==desc
+	this.sortFunctions={};	//	you can add to this if needed.
+};
+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=373998&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 Tue Jan 31 21:39:49 2006
@@ -0,0 +1,516 @@
+/*
+	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
+
+		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");
+		}
+
+		// create the draggers
+
+		for(var i=0; i<this.children.length-1; i++){
+
+
+			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]);
+
+		}
+
+		// 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();
+	},
+
+	layoutPanels: function(){
+
+		//
+		// 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
+		//
+
+		var pos = 0;
+		var size = this.children[0].sizeActual;
+		this.movePanel(this.children[0].domNode, pos, size);
+		this.children[0].position = pos;
+		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;
+			pos += size;
+		}
+		
+		//
+		// if children are widgets, then let them resize themselves (if they want to)
+		//
+		for(var i=0; i<this.children.length; i++){
+			this.children[i].onResized();
+		}
+	},
+
+	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");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgButton.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,141 @@
+/*
+	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
+*/
+
+// FIXME: not yet functional
+
+dojo.provide("dojo.widget.SvgButton");
+
+dojo.require("dojo.widget.Button");
+
+dojo.widget.SvgButton = function(){
+	// FIXME: this is incomplete and doesn't work yet
+	// if DOMButton turns into a mixin, we should subclass Button instead and
+	// just mix in the DOMButton properties.
+
+	dojo.widget.DomButton.call(this);
+	dojo.widget.SvgWidget.call(this);
+
+	// FIXME: freaking implement this already!
+	this.onFoo = function(){ alert("bar"); }
+
+	this.label = "huzzah!";
+
+	this.setLabel = function(x, y, textSize, label, shape){
+		//var labelNode = this.domNode.ownerDocument.createTextNode(this.label);
+		//var textNode = this.domNode.ownerDocument.createElement("text");
+		var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape);
+		var textString = "";
+		switch(shape) {
+			case "ellipse":
+				textString = "<text x='"+ coords[6] + "' y='"+ coords[7] + "'>"+ label + "</text>";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+			case "rectangle":
+				//FIXME: implement
+				textString = "";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+			case "circle":
+				//FIXME: implement
+				textString = "";
+				//textNode.setAttribute("x", coords[6]);
+				//textNode.setAttribute("y", coords[7]);
+				break;
+		}
+		//textNode.appendChild(labelNode);
+		//this.domNode.appendChild(textNode);
+		return textString;
+		alert(textNode.getComputedTextLength());
+	}
+
+	this.fillInTemplate = function(x, y, textSize, label, shape){
+		// the idea is to set the text to the appropriate place given its length
+		// and the template shape
+		
+		// FIXME: For now, assuming text sizes are integers in SVG units
+		this.textSize = textSize || 12;
+		this.label = label;
+		// FIXEME: for now, I'm going to fake this... need to come up with a real way to 
+		// determine the actual width of the text, such as computedStyle
+		var textWidth = this.label.length*this.textSize ;
+		//this.setLabel();
+	}
+}
+
+dojo.inherits(dojo.widget.SvgButton, dojo.widget.DomButton);
+
+// FIXME
+dojo.widget.SvgButton.prototype.shapeString = function(x, y, textSize, label, shape) {
+	switch(shape) {
+		case "ellipse":
+			var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape)
+			return "<ellipse cx='"+ coords[4]+"' cy='"+ coords[5]+"' rx='"+ coords[2]+"' ry='"+ coords[3]+"'/>";
+			break;
+		case "rect":
+			//FIXME: implement
+			return "";
+			//return "<rect x='110' y='45' width='70' height='30'/>";
+			break;
+		case "circle":
+			//FIXME: implement
+			return "";
+			//return "<circle cx='210' cy='60' r='23'/>";
+			break;
+	}
+}
+
+dojo.widget.SvgButton.prototype.coordinates = function(x, y, textSize, label, shape) {
+	switch(shape) {
+		case "ellipse":
+			var buttonWidth = label.length*textSize;
+			var buttonHeight = textSize*2.5
+			var rx = buttonWidth/2;
+			var ry = buttonHeight/2;
+			var cx = rx + x;
+			var cy = ry + y;
+			var textX = cx - rx*textSize/25;
+			var textY = cy*1.1;
+			return [buttonWidth, buttonHeight, rx, ry, cx, cy, textX, textY];
+			break;
+		case "rectangle":
+			//FIXME: implement
+			return "";
+			break;
+		case "circle":
+			//FIXME: implement
+			return "";
+			break;
+	}
+}
+
+dojo.widget.SvgButton.prototype.labelString = function(x, y, textSize, label, shape){
+	var textString = "";
+	var coords = dojo.widget.SvgButton.prototype.coordinates(x, y, textSize, label, shape);
+	switch(shape) {
+		case "ellipse":
+			textString = "<text x='"+ coords[6] + "' y='"+ coords[7] + "'>"+ label + "</text>";
+			break;
+		case "rectangle":
+			//FIXME: implement
+			textString = "";
+			break;
+		case "circle":
+			//FIXME: implement
+			textString = "";
+			break;
+	}
+	return textString;
+}
+
+dojo.widget.SvgButton.prototype.templateString = function(x, y, textSize, label, shape) {
+	return "<g class='dojoButton' dojoAttachEvent='onClick; onMouseMove: onFoo;' dojoAttachPoint='labelNode'>"+ dojo.widgets.SVGButton.prototype.shapeString(x, y, textSize, label, shape) + dojo.widget.SVGButton.prototype.labelString(x, y, textSize, label, shape) + "</g>";
+}

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/SvgWidget.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,92 @@
+/*
+	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.require("dojo.widget.DomWidget");
+dojo.provide("dojo.widget.SvgWidget");
+dojo.provide("dojo.widget.SVGWidget"); // back compat
+
+dojo.require("dojo.dom");
+
+// SVGWidget is a mixin ONLY
+dojo.widget.SvgWidget = function(args){
+	// mix in the parent type
+	// dojo.widget.DomWidget.call(this);
+}
+dojo.inherits(dojo.widget.SvgWidget, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.SvgWidget, {
+	getContainerHeight: function(){
+		// NOTE: container height must be returned as the INNER height
+		dj_unimplemented("dojo.widget.SvgWidget.getContainerHeight");
+	},
+
+	getContainerWidth: function(){
+		// return this.parent.domNode.offsetWidth;
+		dj_unimplemented("dojo.widget.SvgWidget.getContainerWidth");
+	},
+
+	setNativeHeight: function(height){
+		// var ch = this.getContainerHeight();
+		dj_unimplemented("dojo.widget.SVGWidget.setNativeHeight");
+	},
+
+	createNodesFromText: function(txt, wrap){
+		return dojo.dom.createNodesFromText(txt, wrap);
+	}
+});
+
+dojo.widget.SVGWidget = dojo.widget.SvgWidget;
+
+try{
+(function(){
+	var tf = function(){
+		// FIXME: fill this in!!!
+		var rw = new function(){
+			dojo.widget.SvgWidget.call(this);
+			this.buildRendering = function(){ return; }
+			this.destroyRendering = function(){ return; }
+			this.postInitialize = function(){ return; }
+			this.cleanUp = function(){ return; }
+			this.widgetType = "SVGRootWidget";
+			this.domNode = document.documentElement;
+		}
+		var wm = dojo.widget.manager;
+		wm.root = rw;
+		wm.add(rw);
+
+		// extend the widgetManager with a getWidgetFromNode method
+		wm.getWidgetFromNode = function(node){
+			var filter = function(x){
+				if(x.domNode == node){
+					return true;
+				}
+			}
+			var widgets = [];
+			while((node)&&(widgets.length < 1)){
+				widgets = this.getWidgetsByFilter(filter);
+				node = node.parentNode;
+			}
+			if(widgets.length > 0){
+				return widgets[0];
+			}else{
+				return null;
+			}
+		}
+
+		wm.getWidgetFromEvent = function(domEvt){
+			return this.getWidgetFromNode(domEvt.target);
+		}
+
+		wm.getWidgetFromPrimitive = wm.getWidgetFromNode;
+	}
+	// make sure we get called when the time is right
+	dojo.event.connect(dojo.hostenv, "loaded", tf);
+})();
+}catch(e){ alert(e); }

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TabPane.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,157 @@
+/*
+	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.TabPane");
+dojo.provide("dojo.widget.html.TabPane");
+dojo.provide("dojo.widget.Tab");
+dojo.provide("dojo.widget.html.Tab");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutPane");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+
+//////////////////////////////////////////
+// TabPane -- a set of Tabs
+//////////////////////////////////////////
+dojo.widget.html.TabPane = function() {
+	dojo.widget.html.LayoutPane.call(this);
+}
+dojo.inherits(dojo.widget.html.TabPane, dojo.widget.html.LayoutPane);
+
+dojo.lang.extend(dojo.widget.html.TabPane, {
+	widgetType: "TabPane",
+
+	// Constructor arguments
+	labelPosition: "top",
+	useVisibility: false,		// true-->use visibility:hidden instead of display:none
+
+
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTabPane.css"),
+
+	selectedTab: "",		// initially selected tab (widgetId)
+
+	fillInTemplate: function(args, frag) {
+		dojo.widget.html.TabPane.superclass.fillInTemplate.call(this, args, frag);
+		
+		dojo.style.insertCssFile(this.templateCssPath, null, true);
+		dojo.html.prependClass(this.domNode, "dojoTabPane");
+	},
+
+	postCreate: function(args, frag) {
+		// Create <ul> with special formatting to store all the tab labels
+		// TODO: set "bottom" css tag if label is on bottom
+		this.ul = document.createElement("ul");
+		dojo.html.addClass(this.ul, "tabs");
+		dojo.html.addClass(this.ul, this.labelPosition);
+
+		// Load all the tabs, creating a label for each one
+		for(var i=0; i<this.children.length; i++){
+			this._setupTab(this.children[i]);
+		}
+		dojo.widget.html.TabPane.superclass.postCreate.call(this, args, frag);
+
+		// Put tab labels in a panel on the top (or bottom)
+		this.filterAllowed(this, 'labelPosition', ['top', 'bottom']);
+		this.labelPanel = dojo.widget.createWidget("LayoutPane", {layoutAlign: this.labelPosition});
+		this.labelPanel.domNode.appendChild(this.ul);
+		dojo.widget.html.TabPane.superclass.addChild.call(this, this.labelPanel);
+
+		// workaround CSS loading race condition bug
+		dojo.lang.setTimeout(this, this.onResized, 50);
+	},
+
+	addChild: function(child, overrideContainerNode, pos, ref, insertIndex){
+		this._setupTab(child);
+		dojo.widget.html.TabPane.superclass.addChild.call(this,child, overrideContainerNode, pos, ref, insertIndex);
+	},
+
+	_setupTab: function(tab){
+		tab.layoutAlign = "client";
+		tab.domNode.style.display="none";
+		dojo.html.prependClass(tab.domNode, "dojoTabPanel");
+
+		// Create label
+		tab.li = document.createElement("li");
+		var span = document.createElement("span");
+		span.innerHTML = tab.label;
+		dojo.html.disableSelection(span);
+		tab.li.appendChild(span);
+		this.ul.appendChild(tab.li);
+		
+		var self = this;
+		dojo.event.connect(tab.li, "onclick", function(){ self.selectTab(tab); });
+		
+		if(!this.selectedTabWidget || this.selectedTab==tab.widgetId || tab.selected){
+			this.selectedTabWidget=tab;
+		}
+	},
+
+	selectTab: function(tab) {
+		// Deselect old tab and select new one
+		if (this.selectedTabWidget) {
+			this._hideTab(this.selectedTabWidget);
+		}
+		this.selectedTabWidget = tab;
+		this._showTab(tab);
+		dojo.widget.html.TabPane.superclass.onResized.call(this);
+	},
+	
+	_showTab: function(tab) {
+		dojo.html.addClass(tab.li, "current");
+		tab.selected=true;
+		if ( this.useVisibility && !dojo.render.html.ie ) {
+			tab.domNode.style.visibility="visible";
+		} else {
+			tab.show();
+		}
+	},
+
+	_hideTab: function(tab) {
+		dojo.html.removeClass(tab.li, "current");
+		tab.selected=false;
+		if( this.useVisibility ){
+			tab.domNode.style.visibility="hidden";
+		}else{
+			tab.hide();
+		}
+	},
+
+	onResized: function() {
+		// Display the selected tab
+		if(this.selectedTabWidget){
+			this.selectTab(this.selectedTabWidget);
+		} else {
+			dojo.widget.html.TabPane.superclass.onResized.call(this);
+		}
+	}
+});
+dojo.widget.tags.addParseTreeHandler("dojo:TabPane");
+
+// These arguments can be specified for the children of a TabPane.
+// Since any widget can be specified as a TabPane child, mix them
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	label: "",
+	selected: false	// is this tab currently selected?
+});
+
+// Deprecated class.  TabPane can take any widget as input.
+// Use ContentPane, LayoutPane, etc.
+dojo.widget.html.Tab = function() {
+	dojo.widget.html.LayoutPane.call(this);
+}
+dojo.inherits(dojo.widget.html.Tab, dojo.widget.html.LayoutPane);
+dojo.lang.extend(dojo.widget.html.Tab, {
+	widgetType: "Tab"
+});
+dojo.widget.tags.addParseTreeHandler("dojo:Tab");
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Tabs.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,279 @@
+/*
+	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.Tabs");
+dojo.provide("dojo.widget.html.Tabs");
+
+dojo.deprecated("dojo.widget.Tabs",  "use dojo.widget.TabPane", "0.3");
+
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+
+dojo.widget.tags.addParseTreeHandler("dojo:tabs");
+
+dojo.widget.html.Tabs = function() {
+	dojo.widget.HtmlWidget.call(this);
+	this.tabs = [];
+	this.panels = [];
+}
+dojo.inherits(dojo.widget.html.Tabs, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.Tabs, {
+
+	widgetType: "Tabs",
+	isContainer: true,
+
+	templatePath: null, // prolly not
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTabs.css"),
+
+	domNode: null,
+	containerNode: null,
+
+	selected: -1,
+
+	tabTarget: "",
+	extractContent: false, // find the bits inside <body>
+	parseContent: false, // parse externally loaded pages for widgets
+
+	preventCache: false,
+
+	buildRendering: function(args, frag) {
+		dojo.style.insertCssFile(this.templateCssPath);
+		this.domNode = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+		if(!this.domNode) { dj_error("html.Tabs: No node reference"); }
+
+		if(args["tabtarget"]) {
+			this.tabtarget = args["tabtarget"];
+			this.containerNode = document.getElementById(args["tabtarget"]);
+		} else {
+			this.containerNode = document.createElement("div");
+			var next = this.domNode.nextSibling;
+			if(next) {
+				this.domNode.parentNode.insertBefore(this.containerNode, next);
+			} else {
+				this.domNode.parentNode.appendChild(this.containerNode);
+			}
+		}
+		dojo.html.addClass(this.containerNode, "dojoTabPanelContainer");
+
+		var li = dojo.dom.getFirstChildElement(this.domNode);
+		while(li) {
+			var a = li.getElementsByTagName("a").item(0);
+			this.addTab(a);
+			li = dojo.dom.getNextSiblingElement(li);
+		}
+
+		if(this.selected == -1) { this.selected = 0; }
+		this.selectTab(null, this.tabs[this.selected]);
+	},
+
+	addTab: function(title, url, tabId, tabHandler) {
+		// TODO: make this an object proper
+		var panel = {
+			url: null,
+			title: null,
+			isLoaded: false,
+			id: null,
+			isLocal: false
+		};
+
+		function isLocal(a) {
+			var url = a.getAttribute("href");
+			var hash = url.indexOf("#");
+			if(hash == 0) {
+				return true;
+			}
+			var loc = location.href.split("#")[0];
+			var url2 = url.split("#")[0];
+			if(loc == url2) {
+				return true;
+			}
+			if(unescape(loc) == url2) {
+				return true;
+			}
+			var outer = a.outerHTML;
+			if(outer && /href=["']?#/i.test(outer)) {
+				return true;
+			}
+			return false;
+		}
+
+		if(title && title.tagName && title.tagName.toLowerCase() == "a") {
+			// init case
+			var a = title;
+			var li = a.parentNode;
+			title = a.innerHTML;
+			url = a.getAttribute("href");
+			var id = null;
+			var hash = url.indexOf("#");
+			if(isLocal(a)) {
+				id = url.split("#")[1];
+				dj_debug("setting local id:", id);
+				url = "#" + id;
+				panel.isLocal = true;
+			} else {
+				id = a.getAttribute("tabid");
+			}
+
+			panel.url = url;
+			panel.title = title;
+			panel.id = id || dojo.html.getUniqueId();
+			dj_debug("panel id:", panel.id, "url:", panel.url);
+		} else {
+			// programmatically adding
+			var li = document.createElement("li");
+			var a = document.createElement("a");
+			a.innerHTML = title;
+			a.href = url;
+			li.appendChild(a);
+			this.domNode.appendChild(li);
+
+			panel.url = url;
+			panel.title = title;
+			panel.id = tabId || dojo.html.getUniqueId();
+			dj_debug("prg tab:", panel.id, "url:", panel.url);
+		}
+
+		if(panel.isLocal) {
+			var node = document.getElementById(id);
+			node.style.display = "none";
+			this.containerNode.appendChild(node);
+		} else {
+			var node = document.createElement("div");
+			node.style.display = "none";
+			node.id = panel.id;
+			this.containerNode.appendChild(node);
+		}
+
+		var handler = a.getAttribute("tabhandler") || tabHandler;
+		if(handler) {
+			this.setPanelHandler(handler, panel);
+		}
+
+		dojo.event.connect(a, "onclick", this, "selectTab");
+
+		this.tabs.push(li);
+		this.panels.push(panel);
+
+		if(this.selected == -1 && dojo.html.hasClass(li, "current")) {
+			this.selected = this.tabs.length-1;
+		}
+
+		return { "tab": li, "panel": panel };
+	},
+
+	selectTab: function(e, target) {
+		if(dojo.lang.isNumber(e)) {
+			target = this.tabs[e];
+		}
+		else if(e) {
+			if(e.target) {
+				target = e.target;
+				while(target && (target.tagName||"").toLowerCase() != "li") {
+					target = target.parentNode;
+				}
+			}
+			if(e.preventDefault) { e.preventDefault(); }
+		}
+
+		dojo.html.removeClass(this.tabs[this.selected], "current");
+
+		for(var i = 0; i < this.tabs.length; i++) {
+			if(this.tabs[i] == target) {
+				dojo.html.addClass(this.tabs[i], "current");
+				this.selected = i;
+				break;
+			}
+		}
+
+		var panel = this.panels[this.selected];
+		if(panel) {
+			this.getPanel(panel);
+			this.hidePanels(panel);
+			document.getElementById(panel.id).style.display = "";
+		}
+	},
+
+	setPanelHandler: function(handler, panel) {
+		var fcn = dojo.lang.isFunction(handler) ? handler : window[handler];
+		if(!dojo.lang.isFunction(fcn)) {
+			throw new Error("Unable to set panel handler, '" + handler + "' not a function.");
+			return;
+		}
+		this["tabHandler" + panel.id] = function() {
+			return fcn.apply(this, arguments);
+		}
+	},
+
+	runPanelHandler: function(panel) {
+		if(dojo.lang.isFunction(this["tabHandler" + panel.id])) {
+			this["tabHandler" + panel.id](panel, document.getElementById(panel.id));
+			return false;
+		}
+		return true;
+	},
+
+	getPanel: function(panel) {
+		if(this.runPanelHandler(panel)) {
+			if(panel.isLocal) {
+				// do nothing?
+			} else {
+				if(!panel.isLoaded || !this.useCache) {
+					this.setExternalContent(panel, panel.url, this.useCache, this.preventCache);
+				}
+			}
+		}
+	},
+
+	setExternalContent: function(panel, url, useCache, preventCache) {
+		var node = document.getElementById(panel.id);
+		node.innerHTML = "Loading...";
+
+		var extract = this.extractContent;
+		var parse = this.parseContent;
+
+		dojo.io.bind({
+			url: url,
+			useCache: useCache,
+			preventCache: preventCache,
+			mimetype: "text/html",
+			handler: function(type, data, e) {
+				if(type == "load") {
+					if(extract) {
+						var matches = data.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+						if(matches) { data = matches[1]; }
+					}
+					node.innerHTML = data;
+					panel.isLoaded = true;
+					if(parse) {
+						var parser = new dojo.xml.Parse();
+						var frag = parser.parseElement(node, null, true);
+						dojo.widget.getParser().createComponents(frag);
+					}
+				} else {
+					node.innerHTML = "Error loading '" + panel.url + "' (" + e.status + " " + e.statusText + ")";
+				}
+			}
+		});
+	},
+
+	hidePanels: function(except) {
+		for(var i = 0; i < this.panels.length; i++) {
+			if(this.panels[i] != except && this.panels[i].id) {
+				var p = document.getElementById(this.panels[i].id);
+				if(p) {
+					p.style.display = "none";
+				}
+			}
+		}
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TaskBar.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,32 @@
+/*
+	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.TaskBar");
+dojo.provide("dojo.widget.TaskBarItem");
+dojo.require("dojo.widget.Widget");
+
+dojo.widget.TaskBar = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "TaskBar";
+	this.isContainer = true;
+}
+dojo.inherits(dojo.widget.TaskBar, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:taskbar");
+
+dojo.widget.TaskBarItem = function(){
+	dojo.widget.Widget.call(this);
+
+	this.widgetType = "TaskBarItem";
+}
+dojo.inherits(dojo.widget.TaskBarItem, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:taskbaritem");
+
+dojo.requireAfterIf("html", "dojo.widget.html.TaskBar");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TemplatedContainer.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TemplatedContainer.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TemplatedContainer.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TemplatedContainer.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,50 @@
+/*
+	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.TemplatedContainer");
+dojo.provide("dojo.widget.html.TemplatedContainer");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+dojo.widget.html.TemplatedContainer = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.TemplatedContainer, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TemplatedContainer, {
+	widgetType: "TemplatedContainer",
+
+	isContainer: true,
+	templateString: '<div><div dojoAttachPoint="header"><hr></div><div dojoAttachPoint="containerNode"></div><div dojoAttachPoint="footer"><hr></div></div>',
+	header: null,
+	containerNode: null,
+	footer: null,
+	domNode: null,
+
+	onResized: function() {
+		// Clients should override this function to do special processing,
+		// then call this.notifyChildrenOfResize() to notify children of resize
+		this.notifyChildrenOfResize();
+	},
+	
+	notifyChildrenOfResize: function() {
+		for(var i=0; i<this.children.length; i++) {
+			var child = this.children[i];
+			//dojo.debug(this.widgetId + " resizing child " + child.widgetId);
+			if ( child.onResized ) {
+				child.onResized();
+			}
+		}
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:TemplatedContainer");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js?rev=373998&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/TimePicker.js Tue Jan 31 21:39:49 2006
@@ -0,0 +1,113 @@
+/*
+	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.TimePicker");
+dojo.provide("dojo.widget.TimePicker.util");
+dojo.require("dojo.widget.DomWidget");
+
+dojo.widget.TimePicker = function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "TimePicker";
+	this.isContainer = false;
+	// the following aliases prevent breaking people using 0.2.x
+	this.toRfcDateTime = dojo.widget.TimePicker.util.toRfcDateTime;
+	this.fromRfcDateTime = dojo.widget.TimePicker.util.fromRfcDateTime;
+	this.toAmPmHour = dojo.widget.TimePicker.util.toAmPmHour;
+	this.fromAmPmHour = dojo.widget.TimePicker.util.fromAmPmHour;
+}
+
+dojo.inherits(dojo.widget.TimePicker, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:timepicker");
+
+dojo.requireAfterIf("html", "dojo.widget.html.TimePicker");
+
+dojo.widget.TimePicker.util = new function() {
+	// utility functions
+	this.toRfcDateTime = function(jsDate) {
+		if(!jsDate) {
+			jsDate = new Date();
+		}
+		var year = jsDate.getFullYear();
+		var month = jsDate.getMonth() + 1;
+		if (month < 10) {
+			month = "0" + month.toString();
+		}
+		var date = jsDate.getDate();
+		if (date < 10) {
+			date = "0" + date.toString();
+		}
+		var hour = jsDate.getHours();
+		if (hour < 10) {
+			hour = "0" + hour.toString();
+		}
+		var minute = jsDate.getMinutes();
+		if (minute < 10) {
+			minute = "0" + minute.toString();
+		}
+		// no way to set seconds, so set to zero
+		var second = "00";
+		var timeZone = jsDate.getTimezoneOffset();
+		var timeZoneHour = parseInt(timeZone/60);
+		if(timeZoneHour > -10 && timeZoneHour < 0) {
+			timeZoneHour = "-0" + Math.abs(timeZoneHour);
+		} else if(timeZoneHour < 10) {
+			timeZoneHour = "+0" + timeZoneHour.toString();
+		} else if(timeZoneHour >= 10) {
+			timeZoneHour = "+" + timeZoneHour.toString();
+		}
+		var timeZoneMinute = timeZone%60;
+		if(timeZoneMinute < 10) {
+			timeZoneMinute = "0" + timeZoneMinute.toString();
+		}
+		return year + "-" + month + "-" + date + "T" + hour + ":" + minute + ":" + second + timeZoneHour +":" + timeZoneMinute;
+	}
+
+	this.fromRfcDateTime = function(rfcDate, useDefaultMinutes) {
+		var tempDate = new Date();
+		if(!rfcDate || !rfcDate.split("T")[1]) {
+			if(useDefaultMinutes) {
+				tempDate.setMinutes(Math.floor(tempDate.getMinutes()/5)*5);
+			} else {
+				tempDate.setMinutes(0);
+			}
+		} else {
+			var tempTime = rfcDate.split("T")[1].split(":");
+			// fullYear, month, date
+			var tempDate = new Date();
+			tempDate.setHours(tempTime[0]);
+			tempDate.setMinutes(tempTime[1]);
+		}
+		return tempDate;
+	}
+
+	this.toAmPmHour = function(hour) {
+		var amPmHour = hour;
+		var isAm = true;
+		if (amPmHour == 0) {
+			amPmHour = 12;
+		} else if (amPmHour>12) {
+			amPmHour = amPmHour - 12;
+			isAm = false;
+		} else if (amPmHour == 12) {
+			isAm = false;
+		}
+		return [amPmHour, isAm];
+	}
+
+	this.fromAmPmHour = function(amPmHour, isAm) {
+		var hour = parseInt(amPmHour, 10);
+		if(isAm && hour == 12) {
+			hour = 0;
+		} else if (!isAm && hour<12) {
+			hour = hour + 12;
+		}
+		return hour;
+	}
+}



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