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/16 00:31:07 UTC

svn commit: r378118 [19/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/html/ComboBox.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ComboBox.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ComboBox.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ComboBox.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,478 @@
+/*
+	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.html.ComboBox");
+dojo.require("dojo.widget.ComboBox");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.fx.*");
+dojo.require("dojo.dom");
+dojo.require("dojo.html");
+dojo.require("dojo.string");
+dojo.require("dojo.widget.html.stabile");
+
+dojo.widget.html.ComboBox = function(){
+	dojo.widget.ComboBox.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	this.autoComplete = true;
+	this.formInputName = "";
+	this.name = ""; // clone in the name from the DOM node
+	this.textInputNode = null;
+	this.comboBoxValue = null;
+	this.comboBoxSelectionValue = null;
+	this.optionsListWrapper = null;
+	this.optionsListNode = null;
+	this.downArrowNode = null;
+	this.cbTableNode = null;
+	this.searchTimer = null;
+	this.searchDelay = 100;
+	this.dataUrl = "";
+	// mode can also be "remote" for JSON-returning live search or "html" for
+	// dumber live search
+	this.mode = "local"; 
+	this.selectedResult = null;
+	this._highlighted_option = null;
+	this._prev_key_backspace = false;
+	this._prev_key_esc = false;
+	this._result_list_open = false;
+	this._gotFocus = false;
+	this._mousewheel_connected = false;
+
+}
+
+dojo.inherits(dojo.widget.html.ComboBox, dojo.widget.HtmlWidget);
+
+// copied from superclass since we can't really over-ride via prototype
+dojo.lang.extend(dojo.widget.html.ComboBox, dojo.widget.ComboBox.defaults);
+
+dojo.lang.extend(dojo.widget.html.ComboBox, {
+
+
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlComboBox.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlComboBox.css"),
+
+	setValue: function(value) {
+		this.comboBoxValue.value = value;
+		if (this.textInputNode.value != value) { // prevent mucking up of selection
+			this.textInputNode.value = value;
+		}
+		dojo.widget.html.stabile.setState(this.widgetId, this.getState(), true);
+	},
+
+	getValue: function() {
+		return this.comboBoxValue.value;
+	},
+
+	getState: function() {
+		return {value: this.getValue()};
+	},
+
+	setState: function(state) {
+		this.setValue(state.value);
+	},
+
+
+	getCaretPos: function(element){
+		// FIXME: we need to figure this out for Konq/Safari!
+		if(dojo.render.html.mozilla){
+			// FIXME: this is totally borked on Moz < 1.3. Any recourse?
+			return element.selectionStart;
+		}else if(dojo.render.html.ie){
+			// in the case of a mouse click in a popup being handled,
+			// then the document.selection is not the textarea, but the popup
+			// var r = document.selection.createRange();
+			// hack to get IE 6 to play nice. What a POS browser.
+			var tr = document.selection.createRange().duplicate();
+			var ntr = element.createTextRange();
+			tr.move("character",0);
+			ntr.move("character",0);
+			try {
+				// If control doesnt have focus, you get an exception.
+				// Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
+				// There appears to be no workaround for this - googled for quite a while.
+				ntr.setEndPoint("EndToEnd", tr);
+				return String(ntr.text).replace(/\r/g,"").length;
+			} catch (e) {
+				return 0; // If focus has shifted, 0 is fine for caret pos.
+			}
+			
+		}
+	},
+
+	setCaretPos: function(element, location){
+		location = parseInt(location);
+		this.setSelectedRange(element, location, location);
+	},
+
+	setSelectedRange: function(element, start, end){
+		if(!end){ end = element.value.length; }  // NOTE: Strange - should be able to put caret at start of text?
+		// Mozilla
+		// parts borrowed from http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
+		if(element.setSelectionRange){
+			element.focus();
+			element.setSelectionRange(start, end);
+		}else if(element.createTextRange){ // IE
+			var range = element.createTextRange();
+			with(range){
+				collapse(true);
+				moveEnd('character', end);
+				moveStart('character', start);
+				select();
+			}
+		}else{ //otherwise try the event-creation hack (our own invention)
+			// do we need these?
+			element.value = element.value;
+			element.blur();
+			element.focus();
+			// figure out how far back to go
+			var dist = parseInt(element.value.length)-end;
+			var tchar = String.fromCharCode(37);
+			var tcc = tchar.charCodeAt(0);
+			for(var x = 0; x < dist; x++){
+				var te = document.createEvent("KeyEvents");
+				te.initKeyEvent("keypress", true, true, null, false, false, false, false, tcc, tcc);
+				twe.dispatchEvent(te);
+			}
+		}
+	},
+
+	onKeyDown: function(evt){
+		if (evt.ctrlKey || evt.altKey) {
+			return;
+		}
+		// These need to be detected in onKeyDown so that the events can be cancelled.
+		// Perhaps use dojo.event.browser.keys.KEY_ENTER etc? 
+		if(this._result_list_open && ((evt.keyCode == 32)||(evt.keyCode == 13))){ // space is 32, enter is 13.
+			this.selectOption();
+			dojo.event.browser.stopEvent(evt);
+			return;
+		}else if(evt.keyCode == 40){ // down is 40
+			if(!this._result_list_open){
+				this.startSearchFromInput();
+			}
+			this.highlightNextOption();
+			dojo.event.browser.stopEvent(evt);
+			return;
+		}else if(evt.keyCode == 38){ // up is 38
+			this.highlightPrevOption();
+			dojo.event.browser.stopEvent(evt);
+			return;
+		}
+	},
+
+	onKeyUp: function(evt){
+		if (evt.ctrlKey || evt.altKey) {
+			return;
+		}
+		if((evt.keyCode == 32)||(evt.keyCode == 13)||(evt.keyCode == 40)||(evt.keyCode == 38)){
+			return;
+		}
+		// FIXME Everything below here should really be moved into onKeyDown?
+		if(evt.keyCode == 27){ // esc is 27
+			this.hideResultList();
+			if(this._prev_key_esc){
+				// FIXME - probably get rid of?
+				// Shouldnt deselect if something was selected on entry to form. Also it doesnt clear the input text for IE.
+				// And shouldnt lose focus - users are likely to press escape more than once when they want to cancel something.
+				/* 
+				this.textInputNode.blur();
+				this.selectedResult = null;
+				*/
+			}
+			this._prev_key_esc = true;
+			return;
+		}else{
+			this.setValue(this.textInputNode.value);
+		}
+
+		// backspace is 8
+		this._prev_key_backspace = (evt.keyCode == 8) ? true : false;
+		this._prev_key_esc = false;
+
+		if(this.searchTimer){
+			clearTimeout(this.searchTimer);
+		}
+		if((this._prev_key_backspace)&&(!this.textInputNode.value.length)){
+			this.hideResultList();
+		}else{
+			// FIXME This is untidy. Occurs when tabbing in to a control gives a keyup event and maybe other wierd keys too?
+			this.searchTimer = setTimeout(dojo.lang.hitch(this, this.startSearchFromInput), this.searchDelay);
+		}
+	},
+
+	setSelectedValue: function(value){
+		// FIXME, not sure what to do here!
+		this.comboBoxSelectionValue.value = value;
+		this.hideResultList();
+	},
+
+	highlightNextOption: function(){
+		if(this._highlighted_option){
+			dojo.html.removeClass(this._highlighted_option, "cbItemHighlight");
+		}
+		if((!this._highlighted_option)||(!this._highlighted_option.nextSibling)){
+			this._highlighted_option = this.optionsListNode.firstChild;
+		}else{
+			this._highlighted_option = this._highlighted_option.nextSibling;
+		}
+		dojo.html.addClass(this._highlighted_option, "cbItemHighlight");
+	},
+
+	highlightPrevOption: function(){
+		if(this._highlighted_option){
+			dojo.html.removeClass(this._highlighted_option, "cbItemHighlight");
+		}
+		if((!this._highlighted_option)||(!this._highlighted_option.previousSibling)){
+			this._highlighted_option = this.optionsListNode.lastChild;
+		}else{
+			this._highlighted_option = this._highlighted_option.previousSibling;
+		}
+		dojo.html.addClass(this._highlighted_option, "cbItemHighlight");
+	},
+
+	onMouseWheel: function(evt) {
+		if (this._hasFocus) {
+			if (evt.wheelDelta < 0) {
+				if(!this._result_list_open){
+					this.startSearchFromInput();
+				}
+				this.highlightNextOption();
+			} else if (evt.wheelDelta > 0) {
+				this.highlightPrevOption();
+			}
+			evt.preventDefault(); // so don't scroll window
+			return false;
+		};
+	},
+  
+	fillInTemplate: function(args, frag){
+		// FIXME: need to get/assign DOM node names for form participation here.
+		this.comboBoxValue.name = this.name;
+		this.comboBoxSelectionValue.name = this.name+"_selected";
+		
+		// NOTE: this doesn't copy style info inherited from classes;
+		// it's just primitive support for direct style setting
+		var sourceNodeStyle = this.getFragNodeRef(frag).style;
+		if ( sourceNodeStyle ){
+			this.domNode.style.cssText = sourceNodeStyle.cssText;
+		}
+
+		// FIXME: add logic
+		this.dataProvider = new dojo.widget.ComboBoxDataProvider();
+
+		if(!dojo.string.isBlank(this.dataUrl)){
+			if("local" == this.mode){
+				var _this = this;
+				dojo.io.bind({
+					url: this.dataUrl,
+					load: function(type, data, evt){ 
+						if(type=="load"){
+							_this.dataProvider.setData(data);
+						}
+					},
+					mimetype: "text/javascript"
+				});
+			}else if("remote" == this.mode){
+				this.dataProvider = new dojo.widget.incrementalComboBoxDataProvider(this.dataUrl);
+			}
+		}else{
+			// check to see if we can populate the list from <option> elements
+			var node = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+			if((node)&&(node.nodeName.toLowerCase() == "select")){
+				// NOTE: we're not handling <optgroup> here yet
+				var opts = node.getElementsByTagName("option");
+				var ol = opts.length;
+				var data = [];
+				for(var x=0; x<ol; x++){
+					data.push([new String(opts[x].innerHTML), new String(opts[x].value)]);
+				}
+				this.dataProvider.setData(data);
+			}
+		}
+
+		// Prevent IE bleed-through problem
+		this.bgIframe = new dojo.html.BackgroundIframe();
+		if(this.bgIframe.iframe){
+			this.optionsListWrapper.appendChild(this.bgIframe.iframe);
+		}
+	},
+
+	openResultList: function(results){
+		this.clearResultList();
+		if(!results.length){
+			this.hideResultList();
+		}else{
+			this.showResultList();
+		}
+		if(	(this.autoComplete)&&
+			(results.length)&&
+			(!this._prev_key_backspace)&&
+			(this.textInputNode.value.length > 0)){
+			var cpos = this.getCaretPos(this.textInputNode);
+			// only try to extend if we added the last charachter at the end of the input
+			if((cpos+1) >= this.textInputNode.value.length){
+				this.textInputNode.value = results[0][0];
+				// build a new range that has the distance from the earlier
+				// caret position to the end of the first string selected
+				this.setSelectedRange(this.textInputNode, cpos, this.textInputNode.value.length);
+			}
+		}
+
+		var even = true;
+		while(results.length){
+			var tr = results.shift();
+			if(tr){
+				var td = document.createElement("div");
+				td.appendChild(document.createTextNode(tr[0]));
+				td.setAttribute("resultName", tr[0]);
+				td.setAttribute("resultValue", tr[1]);
+				td.className = "cbItem "+((even) ? "cbItemEven" : "cbItemOdd");
+				even = (!even);
+				this.optionsListNode.appendChild(td);
+			}
+		}
+
+		// prevent IE bleed through
+		dojo.lang.setTimeout(this, "showBackgroundIframe", 100);
+	},
+
+	onFocusInput: function(){
+		this._hasFocus = true;
+		if (dojo.render.html.ie && !this._mousewheel_connected) {
+			dojo.event.connect(document, "onmousewheel", this, "onMouseWheel");
+			this._mousewheel_connected = true;
+		}
+	},
+
+	onBlurInput: function(){
+		this._hasFocus = false;
+		if(this.blurTimer){
+			clearTimeout(this.blurTimer);
+		}
+		this.blurTimer = dojo.lang.setTimeout(this, "checkBlurred", 100);
+	},
+
+	checkBlurred: function(){
+		if (!this._hasFocus) {
+			this.hideResultList();
+		}
+		if (this._mousewheel_connected) {
+			dojo.event.disconnect(document, "onmousewheel", this, "onMouseWheel");
+			this._mousewheel_connected = false;
+		}
+	},
+
+	showBackgroundIframe: function(){
+		var w = dojo.style.getOuterWidth(this.optionsListNode);
+		var h = dojo.style.getOuterHeight(this.optionsListNode);
+		if ( isNaN(w) || isNaN(h) ){
+			// need more time to calculate size
+			dojo.lang.setTimeout(this, "showBackgroundIframe", 100);
+			return;
+		}
+		this.bgIframe.show([0,0,w,h]);
+		this.bgIframe.setZIndex(1);
+	},
+
+	selectOption: function(evt){
+		if(!evt){
+			evt = { target: this._highlighted_option };
+		}
+
+		if(!dojo.dom.isDescendantOf(evt.target, this.optionsListNode)){
+			return;
+		}
+
+		var tgt = evt.target;
+		while((tgt.nodeType!=1)||(!tgt.getAttribute("resultName"))){
+			tgt = tgt.parentNode;
+			if(tgt === document.body){
+				return false;
+			}
+		}
+
+		this.textInputNode.value = tgt.getAttribute("resultName");
+		this.selectedResult = [tgt.getAttribute("resultName"), tgt.getAttribute("resultValue")];
+		this.setValue(tgt.getAttribute("resultName"));
+		this.comboBoxSelectionValue.value = tgt.getAttribute("resultValue");
+		this.hideResultList();
+		this.setSelectedRange(this.textInputNode, 0, null);
+		this.tryFocus();
+	},
+
+	clearResultList: function(){
+		var oln = this.optionsListNode;
+		while(oln.firstChild){
+			oln.removeChild(oln.firstChild);
+		}
+	},
+
+	hideResultList: function(){
+		if (this._result_list_open) {
+			this._result_list_open = false;
+			dojo.fx.fadeHide(this.optionsListNode, 200);
+			this.bgIframe.hide();
+		}
+		return;
+	},
+
+	showResultList: function(){
+		if(this._result_list_open){
+		  return;
+		}
+		this._result_list_open = true;
+		with(this.optionsListNode.style){
+			display = "";
+			// visibility = "hidden";
+			height = "";
+			width = dojo.html.getInnerWidth(this.downArrowNode)+dojo.html.getInnerWidth(this.textInputNode)+"px";
+			if(dojo.render.html.khtml){
+				marginTop = dojo.html.totalOffsetTop(this.optionsListNode.parentNode)+"px";
+			}
+		}
+		dojo.html.setOpacity(this.optionsListNode, 0);
+		dojo.fx.fadeIn(this.optionsListNode, 200);
+	},
+
+	handleArrowClick: function(){
+		this.tryFocus();
+		if(this._result_list_open){
+			this.hideResultList();
+		}else{
+			this.startSearchFromInput();
+		}
+	},
+
+	tryFocus: function(){
+		try {    
+			this.textInputNode.focus();
+		} catch (e) {
+			// element isnt focusable if disabled, or not visible etc - not easy to test for.
+ 		};
+	},
+	
+	startSearchFromInput: function(){
+		this.startSearch(this.textInputNode.value);
+	},
+
+	postCreate: function(){
+		dojo.event.connect(this, "startSearch", this.dataProvider, "startSearch");
+		dojo.event.connect(this.dataProvider, "provideSearchResults", this, "openResultList");
+		dojo.event.connect(this.textInputNode, "onblur", this, "onBlurInput");
+		dojo.event.connect(this.textInputNode, "onfocus", this, "onFocusInput");
+
+		var s = dojo.widget.html.stabile.getState(this.widgetId);
+		if (s) {
+			this.setState(s);
+		}
+	}
+
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Container.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Container.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Container.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Container.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,30 @@
+/*
+	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.html.Container");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.Container");
+
+dojo.deprecated("dojo.widget.Container",  "just set isContainer:true", "0.4");
+
+dojo.widget.html.Container = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.Container, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.Container, {
+	widgetType: "Container",
+
+	isContainer: true
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:Container");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContentPane.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContentPane.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContentPane.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContentPane.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,215 @@
+/*
+	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.html.ContentPane");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.ContentPane");
+
+dojo.widget.html.ContentPane = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.ContentPane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.ContentPane, {
+	widgetType: "ContentPane",
+	isContainer: true,
+
+	// remote loading options
+	href: "",
+	extractContent: true,
+	parseContent: true,
+	cacheContent: true,
+	executeScripts: false,
+	preload: false,			// force load of data even if pane is hidden
+	refreshOnShow: false,
+	handler: "",			// generate pane content from a java function
+
+
+	postCreate: function(args, frag, parentComp){
+		if ( this.handler != "" ){
+			this.setHandler(this.handler);
+		}
+		if(this.preload){ this.loadContents(); }
+	},
+
+	onResized: function(){
+		if(this.isVisible()){
+			this.loadContents();
+		}
+		dojo.widget.html.ContentPane.superclass.onResized.call(this);
+	},
+
+	show: function(){
+		// if refreshOnShow is true, reload the contents every time; otherwise, load only the first time
+		if(this.refreshOnShow){
+			this.refresh();
+		}else{
+			this.loadContents();
+		}
+		dojo.widget.html.ContentPane.superclass.show.call(this);
+	},
+
+	refresh: function(){
+		this.isLoaded=false;
+		this.loadContents();
+	},
+
+	loadContents: function() {
+		if ( this.isLoaded ){
+			return;
+		}
+		this.isLoaded=true;
+		if ( dojo.lang.isFunction(this.handler)) {
+			this._runHandler();
+		} else if ( this.href != "" ) {
+			this._downloadExternalContent(this.href, this.cacheContent);
+		}
+	},
+
+	// Reset the (external defined) content of this pane
+	setUrl: function(url) {
+		this.href = url;
+		this.isLoaded = false;
+		if ( this.preload || this.isVisible() ){
+			this.loadContents();
+		}
+	},
+
+	_downloadExternalContent: function(url, useCache) {
+		this.setContent("Loading...");
+		var self = this;
+		dojo.io.bind({
+			url: url,
+			useCache: useCache,
+			mimetype: "text/html",
+			handler: function(type, data, e) {
+				if(type == "load") {
+					self.onLoad.call(self, url, data);
+				} else {
+					self.setContent.call(self, "Error loading '" + url + "' (" + e.status + " " + e.statusText + ")");
+				}
+			}
+		});
+	},
+
+	onLoad: function(url, data){
+		this.setContent(data);
+	},
+
+	setContent: function(data){
+		// remove old children from current content
+		this.destroyChildren();
+
+		var node = this.containerNode || this.domNode;
+		node.innerHTML = data;
+		if(this.executeScripts){
+			data = this._executeScripts(data);
+		}
+		if(this.extractContent) {
+			var matches = data.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
+			if(matches) { data = matches[1]; }
+		}
+		if(this.parseContent){
+			var parser = new dojo.xml.Parse();
+			var frag = parser.parseElement(node, null, true);
+			dojo.widget.getParser().createComponents(frag, this);
+			this.onResized();
+		}
+	},
+
+	// Generate pane content from given java function
+	setHandler: function(handler) {
+		var fcn = dojo.lang.isFunction(handler) ? handler : window[handler];
+		if(!dojo.lang.isFunction(fcn)) {
+			throw new Error("Unable to set handler, '" + handler + "' not a function.");
+			return;
+		}
+		this.handler = function() {
+			return fcn.apply(this, arguments);
+		}
+	},
+
+	_runHandler: function() {
+		if(dojo.lang.isFunction(this.handler)) {
+			this.handler(this, this.domNode);
+			return false;
+		}
+		return true;
+	},
+
+	_executeScripts: function(content) {
+		// handle <script src="foo"> first
+		var src = new RegExp('<script.*?src=".*?"');
+		var repl = new RegExp('<script.*?src="');
+		var matches = src.exec(content);
+		var semaphore = 0;
+	   
+		if (matches != null)
+		{
+			for (i = 0; i < matches.length; i++)
+			{
+				// get the src of the script
+				var scriptSrc = matches[i].replace(repl, '');
+				scriptSrc = scriptSrc.substring(0, scriptSrc.length-1);
+	
+				// this evals remote scripts
+				dojo.io.bind({
+					url:      scriptSrc,
+					load:     function(type, evaldObj) {/* do nothing */ },
+					error:    function(type, error) {alert(type); alert(error); /* do nothing */ },
+					mimetype: "text/javascript",
+					sync:     true
+				});
+			}   
+		}
+	
+		// Remove the script tags we matched
+		repl = new RegExp('<script.*?src=".*?".*?</script>');
+		content = content.replace(repl, '');
+
+		// Next, handle inline scripts
+
+		// Clean up content: remove inline script  comments
+		repl = new RegExp('//.*?$', 'gm');
+		content = content.replace(repl, '\n');
+
+		// Clean up content: remove carraige returns
+		repl = new RegExp('[\n\r]', 'g');
+		content = content.replace(repl, ' ');
+	   
+		// Match anything inside <script> tags
+		src = new RegExp('<script.*?</script>', 'g');
+		matches = content.match(src);
+
+		// For each match that is found...
+		if (matches != null)
+		{
+			for (i = 0; i < matches.length; i++)
+			{
+				// Remove begin tag
+				var repl = new RegExp('<script.*?>', 'gm');
+				var script = matches[i].replace(repl, '');
+
+				// Remove end tag
+				repl = new RegExp('</script>', 'gm');
+				script = script.replace(repl, '');
+	
+				// Execute commands
+				eval(script);
+			}   
+		}
+		return content;
+	}
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:ContentPane");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContextMenu.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContextMenu.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContextMenu.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/ContextMenu.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,166 @@
+/*
+	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.html.ContextMenu");
+dojo.require("dojo.html");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.ContextMenu");
+dojo.require("dojo.lang");
+
+dojo.widget.html.ContextMenu = function(){
+	dojo.widget.ContextMenu.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	this.isShowing = 0;
+	this.templatePath = dojo.uri.dojoUri("src/widget/templates/HtmlContextMenuTemplate.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/Menu.css");
+
+	this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it only responds to those nodes
+
+	// default event detection method 
+	var eventType = "oncontextmenu"; 
+
+	var doc = document.documentElement  || document.body; 
+
+	var _blockHide = false; 
+
+	this.fillInTemplate = function(args, frag){
+
+		var func = "onOpen";
+		var attached = false;
+
+		// connect with rightclick if oncontextmenu is not around
+		// NOTE: It would be very nice to have a dojo.event.browser.supportsEvent here
+		// NOTE: Opera does not have rightclick events, it is listed here only because
+		//     it bails out when connecting with oncontextmenu event
+
+		if((dojo.render.html.khtml && !dojo.render.html.safari) || (dojo.render.html.opera)){
+			eventType = "onmousedown";
+			func = "_checkRightClick";
+		}
+
+		// attach event listeners to our selected nodes
+		for(var i=0; i<this.targetNodeIds.length; i++){
+			var node = document.getElementById(this.targetNodeIds[i]);
+			if(node){
+				dojo.event.connect(node, eventType, this, func);
+				attached = true;
+			}else{
+				// remove this nodeId
+				dj_debug("Couldent find "+this.targetNodeIds[i]+", cant do ContextMenu on this node");
+				this.targetNodeIds.splice(i,1);
+			}
+		}
+
+		// if we got attached to a node, hide on all non node contextevents
+		if(attached){ func = "_canHide"; }
+
+		dojo.event.connect(doc, eventType, this, func);
+	}
+
+	this.onOpen = function(evt){
+		// if (this.isShowing){ this.onHide(evt); } // propably not needed
+		this.isShowing = 1;
+
+		// if I do this, I cant preventDefault in khtml
+		//evt = dojo.event.browser.fixEvent(evt);
+ 
+		// stop default contextmenu, needed in khtml
+		if (evt.preventDefault){ evt.preventDefault(); }
+
+		// need to light up this one before we check width and height
+		this.domNode.style.left = "-9999px";
+		this.domNode.style.top  = "-9999px";
+		this.domNode.style.display = "block";
+
+		// calculate if menu is going to apear within window
+		// or if its partially out of visable area
+		with(dojo.html){
+
+			var menuW = getInnerWidth(this.domNode);
+			var menuH = getInnerHeight(this.domNode);
+
+			var viewport = getViewportSize();
+			var scrolloffset = getScrollOffset();
+		}
+
+		var minX = viewport[0];
+		var minY = viewport[1];
+
+		var maxX = (viewport[0] + scrolloffset[0]) - menuW;
+		var maxY = (viewport[1] + scrolloffset[1]) - menuH;
+
+		var posX = evt.clientX + scrolloffset[0];
+		var posY = evt.clientY + scrolloffset[1];
+
+		if (posX > maxX){ posX = posX - menuW; }
+		if (posY > maxY){ posY = posY - menuH; }
+
+		this.domNode.style.left = posX + "px";
+		this.domNode.style.top = posY + "px";
+
+
+		// block the onclick that follows this particular right click
+		// not if the eventtrigger is documentElement and always when
+		// we use onmousedown hack
+		_blockHide = (evt.currentTarget!=doc || eventType=='onmousedown');
+
+		//return false; // we propably doesnt need to return false as we dont stop the event as we did before
+	}
+
+	/*
+	* _canHide is meant to block the onHide call that follows the event that triggered
+	* onOpen. This is (hopefully) faster that event.connect and event.disconnect every
+	* time the code executes and it makes connecting with onmousedown event possible
+	* and we dont have to stop the event from bubbling further.
+	*
+	* this code is moved into a separete function because it makes it possible for the
+	* user to connect to a onHide event, if anyone would like that.
+	*/
+
+	this._canHide = function(evt){
+		// block the onclick that follows the same event that turn on contextmenu
+		if(_blockHide){
+			// the onclick check is needed to prevent displaying multiple
+			// menus when we have 2 or more contextmenus loaded and are using
+			// the onmousedown hack
+			if(evt.type=='click' || eventType=='oncontextmenu'){
+				_blockHide = false;
+				return;
+			}else{
+				return;
+			}
+		}
+
+		this.onHide(evt);
+	}
+	
+	this.onHide = function(evt){
+		// FIXME: use whatever we use to do more general style setting?
+		this.domNode.style.display = "none";
+		//dojo.event.disconnect(doc, "onclick", this, "onHide");
+		this.isShowing = 0;
+	}
+
+	// callback for rightclicks, needed for browsers that doesnt implement oncontextmenu, konqueror and more? 
+	this._checkRightClick = function(evt){ 
+
+		// for some reason konq comes here even when we are not clicking on the attached nodes 
+		// added check for targetnode 
+		if (evt.button==2 && (this.targetNodeIds.length==0 || (evt.currentTarget.id!="" && dojo.lang.inArray(this.targetNodeIds, evt.currentTarget.id)))){
+
+			return this.onOpen(evt);
+		}
+	}
+
+	dojo.event.connect(doc, "onclick", this, "_canHide");
+}
+
+dojo.inherits(dojo.widget.html.ContextMenu, dojo.widget.HtmlWidget);

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DatePicker.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DatePicker.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DatePicker.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DatePicker.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,307 @@
+/*
+	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.html.DatePicker");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.DatePicker");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+
+/*
+	Some assumptions:
+	- I'm planning on always showing 42 days at a time, and we can scroll by week,
+	not just by month or year
+	- To get a sense of what month to highlight, I basically initialize on the 
+	first Saturday of each month, since that will be either the first of two or 
+	the second of three months being partially displayed, and then I work forwards 
+	and backwards from that point.
+	Currently, I assume that dates are stored in the RFC 3339 format,
+	because I find it to be most human readable and easy to parse
+	http://www.faqs.org/rfcs/rfc3339.html: 		2005-06-30T08:05:00-07:00
+	FIXME: scroll by week not yet implemented
+*/
+
+
+dojo.widget.html.DatePicker = function(){
+	dojo.widget.DatePicker.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	var _this = this;
+	// today's date, JS Date object
+	this.today = "";
+	// selected date, JS Date object
+	this.date = "";
+	// rfc 3339 date
+	this.storedDate = "";
+	// date currently selected in the UI, stored in year, month, date in the format that will be actually displayed
+	this.currentDate = {};
+	// stored in year, month, date in the format that will be actually displayed
+	this.firstSaturday = {};
+	this.classNames = {
+		previous: "previousMonth",
+		current: "currentMonth",
+		next: "nextMonth",
+		currentDate: "currentDate",
+		selectedDate: "selectedItem"
+	}
+
+	this.templatePath =  dojo.uri.dojoUri("src/widget/templates/HtmlDatePicker.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlDatePicker.css");
+	
+	this.fillInTemplate = function(){
+		this.initData();
+		this.initUI();
+	}
+	
+	this.initData = function() {
+		this.today = new Date();
+		if(this.storedDate && (this.storedDate.split("-").length > 2)) {
+			this.date = dojo.widget.DatePicker.util.fromRfcDate(this.storedDate);
+		} else {
+			this.date = this.today;
+		}
+		// calendar math is simplified if time is set to 0
+		this.today.setHours(0);
+		this.date.setHours(0);
+		var month = this.date.getMonth();
+		var tempSaturday = dojo.widget.DatePicker.util.initFirstSaturday(this.date.getMonth().toString(), this.date.getFullYear());
+		this.firstSaturday.year = tempSaturday.year;
+		this.firstSaturday.month = tempSaturday.month;
+		this.firstSaturday.date = tempSaturday.date;
+	}
+	
+	this.setDate = function(rfcDate) {
+		this.storedDate = rfcDate;
+	}
+	
+	
+	this.initUI = function() {
+		this.selectedIsUsed = false;
+		this.currentIsUsed = false;
+		var currentClassName = "";
+		var previousDate = new Date();
+		var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td");
+		var currentCalendarNode;
+		// set hours of date such that there is no chance of rounding error due to 
+		// time change in local time zones
+		previousDate.setHours(8);
+		var nextDate = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date, 8);
+
+		
+		if(this.firstSaturday.date < 7) {
+			// this means there are days to show from the previous month
+			var dayInWeek = 6;
+			for (var i=this.firstSaturday.date; i>0; i--) {
+				currentCalendarNode = calendarNodes.item(dayInWeek);
+				currentCalendarNode.innerHTML = nextDate.getDate();
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+				dayInWeek--;
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, false);
+			}
+			for(var i=dayInWeek; i>-1; i--) {
+				currentCalendarNode = calendarNodes.item(i);
+				currentCalendarNode.innerHTML = nextDate.getDate();
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "previous"));
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, false);				
+			}
+		} else {
+			nextDate.setDate(1);
+			for(var i=0; i<7; i++) {
+				currentCalendarNode = calendarNodes.item(i);
+				currentCalendarNode.innerHTML = i + 1;
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, true);				
+			}
+		}
+		previousDate.setDate(this.firstSaturday.date);
+		previousDate.setMonth(this.firstSaturday.month);
+		previousDate.setFullYear(this.firstSaturday.year);
+		nextDate = this.incrementDate(previousDate, true);
+		var count = 7;
+		currentCalendarNode = calendarNodes.item(count);
+		while((nextDate.getMonth() == previousDate.getMonth()) && (count<42)) {
+			currentCalendarNode.innerHTML = nextDate.getDate();
+			dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+			currentCalendarNode = calendarNodes.item(++count);
+			previousDate = nextDate;
+			nextDate = this.incrementDate(nextDate, true);
+		}
+		
+		while(count < 42) {
+			currentCalendarNode.innerHTML = nextDate.getDate();
+			dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "next"));
+			currentCalendarNode = calendarNodes.item(++count);
+			previousDate = nextDate;
+			nextDate = this.incrementDate(nextDate, true);
+		}
+		this.setMonthLabel(this.firstSaturday.month);
+		this.setYearLabels(this.firstSaturday.year);
+	}
+	
+	this.incrementDate = function(date, bool) {
+		// bool: true to increase, false to decrease
+		var time = date.getTime();
+		var increment = 1000 * 60 * 60 * 24;
+		time = (bool) ? (time + increment) : (time - increment);
+		var returnDate = new Date();
+		returnDate.setTime(time);
+		return returnDate;
+	}
+	
+	this.incrementWeek = function(date, bool) {
+		dojo.unimplemented('dojo.widget.html.DatePicker.incrementWeek');
+	}
+
+	this.incrementMonth = function(date, bool) {
+		dojo.unimplemented('dojo.widget.html.DatePicker.incrementMonth');
+	}
+
+	this.incrementYear = function(date, bool) {
+		dojo.unimplemented('dojo.widget.html.DatePicker.incrementYear');
+	}
+
+	this.onIncrementDate = function(evt) {
+		dojo.unimplemented('dojo.widget.html.DatePicker.onIncrementDate');
+	}
+	
+	this.onIncrementWeek = function(evt) {
+		// FIXME: should make a call to incrementWeek when that is implemented
+		evt.stopPropagation();
+		dojo.unimplemented('dojo.widget.html.DatePicker.onIncrementWeek');
+		switch(evt.target) {
+			case this.increaseWeekNode:
+				break;
+			case this.decreaseWeekNode:
+				break;
+		}
+	}
+
+	this.onIncrementMonth = function(evt) {
+		// FIXME: should make a call to incrementMonth when that is implemented
+		evt.stopPropagation();
+		var month = this.firstSaturday.month;
+		var year = this.firstSaturday.year;
+		switch(evt.currentTarget) {
+			case this.increaseMonthNode:
+				if(month < 11) {
+					month++;
+				} else {
+					month = 0;
+					year++;
+					
+					this.setYearLabels(year);
+				}
+				break;
+			case this.decreaseMonthNode:
+				if(month > 0) {
+					month--;
+				} else {
+					month = 11;
+					year--;
+					this.setYearLabels(year);
+				}
+				break;
+			case this.increaseMonthNode.getElementsByTagName("img").item(0):
+				if(month < 11) {
+					month++;
+				} else {
+					month = 0;
+					year++;
+					this.setYearLabels(year);
+				}
+				break;
+			case this.decreaseMonthNode.getElementsByTagName("img").item(0):
+				if(month > 0) {
+					month--;
+				} else {
+					month = 11;
+					year--;
+					this.setYearLabels(year);
+				}
+				break;
+		}
+		var tempSaturday = dojo.widget.DatePicker.util.initFirstSaturday(month.toString(), year);
+		this.firstSaturday.year = tempSaturday.year;
+		this.firstSaturday.month = tempSaturday.month;
+		this.firstSaturday.date = tempSaturday.date;
+		this.initUI();
+	}
+	
+	this.onIncrementYear = function(evt) {
+		// FIXME: should make a call to incrementYear when that is implemented
+		evt.stopPropagation();
+		var year = this.firstSaturday.year;
+		switch(evt.target) {
+			case this.nextYearLabelNode:
+				year++;
+				break;
+			case this.previousYearLabelNode:
+				year--;
+				break;
+		}
+		var tempSaturday = dojo.widget.DatePicker.util.initFirstSaturday(this.firstSaturday.month.toString(), year);
+		this.firstSaturday.year = tempSaturday.year;
+		this.firstSaturday.month = tempSaturday.month;
+		this.firstSaturday.date = tempSaturday.date;
+		this.initUI();
+	}
+
+	this.setMonthLabel = function(monthIndex) {
+		this.monthLabelNode.innerHTML = this.months[monthIndex];
+	}
+	
+	this.setYearLabels = function(year) {
+		this.previousYearLabelNode.innerHTML = year - 1;
+		this.currentYearLabelNode.innerHTML = year;
+		this.nextYearLabelNode.innerHTML = year + 1;
+	}
+	
+	this.getDateClassName = function(date, monthState) {
+		var currentClassName = this.classNames[monthState];
+		if ((!this.selectedIsUsed) && (date.getDate() == this.date.getDate()) && (date.getMonth() == this.date.getMonth()) && (date.getFullYear() == this.date.getFullYear())) {
+			currentClassName = this.classNames.selectedDate + " " + currentClassName;
+			this.selectedIsUsed = 1;
+		}
+		if((!this.currentIsUsed) && (date.getDate() == this.today.getDate()) && (date.getMonth() == this.today.getMonth()) && (date.getFullYear() == this.today.getFullYear())) {
+			currentClassName = currentClassName + " "  + this.classNames.currentDate;
+			this.currentIsUsed = 1;
+		}
+		return currentClassName;
+	}
+
+	this.onClick = function(evt) {
+		dojo.event.browser.stopEvent(evt)
+	}
+	
+	this.onSetDate = function(evt) {
+		dojo.event.browser.stopEvent(evt);
+		this.selectedIsUsed = 0;
+		this.todayIsUsed = 0;
+		var month = this.firstSaturday.month;
+		var year = this.firstSaturday.year;
+		if (dojo.html.hasClass(evt.target, this.classNames["next"])) {
+			month = ++month % 12;
+			// if month is now == 0, add a year
+			year = (month==0) ? ++year : year;
+		} else if (dojo.html.hasClass(evt.target, this.classNames["previous"])) {
+			month = --month % 12;
+			// if month is now == 0, add a year
+			year = (month==11) ? --year : year;
+		}
+		this.date = new Date(year, month, evt.target.innerHTML);
+		this.setDate(dojo.widget.DatePicker.util.toRfcDate(this.date));
+		this.initUI();
+	}
+}
+dojo.inherits(dojo.widget.html.DatePicker, dojo.widget.HtmlWidget);

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DebugConsole.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DebugConsole.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DebugConsole.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DebugConsole.js Wed Feb 15 15:30:01 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.html.DebugConsole");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.FloatingPane");
+
+// Collection of widgets in a bar, like Windows task bar
+dojo.widget.html.DebugConsole= function(){
+
+	dojo.widget.html.FloatingPane.call(this);
+	dojo.widget.DebugConsole.call(this);
+}
+
+dojo.inherits(dojo.widget.html.DebugConsole, dojo.widget.html.FloatingPane);
+
+dojo.lang.extend(dojo.widget.html.DebugConsole, {
+	postCreate: function() {
+		dojo.widget.html.DebugConsole.superclass.postCreate.call(this);
+		this.clientPane.domNode.id = "debugConsoleClientPane"
+		djConfig.isDebug = true;
+		djConfig.debugContainerId = this.clientPane.domNode.id;
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DropdownButton.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DropdownButton.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DropdownButton.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/DropdownButton.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,188 @@
+/*
+	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 dropdown "smart" so it can't get cutoff on bottom of page, sides of page, etc.
+ */
+
+dojo.provide("dojo.widget.html.DropdownButton");
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.uri.Uri");
+dojo.require("dojo.dom");
+dojo.require("dojo.style");
+dojo.require("dojo.html");
+
+dojo.widget.html.DropdownButton = function() {
+	// mix in the button properties
+	dojo.widget.DropdownButton.call(this);
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.DropdownButton, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.DropdownButton, {
+	
+	// In IE, event handlers on objects inside buttons don't work correctly, so
+	// we just set onClick on the button itself.
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlDropDownButtonTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlButtonTemplate.css"),
+
+	// attach points
+	button: null,
+	table: null,
+	labelCell: null,
+	borderCell: null,
+	arrowCell: null,
+	arrow: null,
+
+	fillInTemplate: function(args, frag) {
+		// input data (containing the anchor for the button itself, plus the
+		// thing to display when you push the down arrow)
+		var input = frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"];
+
+		// Recursively expand widgets inside of the <dojo:dropdownButton>
+		var parser = new dojo.xml.Parse();
+		var frag = parser.parseElement(input, null, true);
+		var ary = dojo.widget.getParser().createComponents(frag);
+
+		this.a = dojo.dom.getFirstChildElement(input);	// the button contents
+		this.menu = dojo.dom.getNextSiblingElement(this.a);	// the menu under the button
+		
+		this.disabled = dojo.html.hasClass(this.a, "disabled");
+		if( this.disabled ) {
+			dojo.html.addClass(this.button, "dojoDisabled");
+			this.domNode.setAttribute("disabled", "true");
+		}
+
+		dojo.html.disableSelection(this.a);
+		this.a.style["text-decoration"]="none";
+		this.labelCell.appendChild(this.a);
+
+		this.arrow.src =
+			dojo.uri.dojoUri("src/widget/templates/images/dropdownButtonsArrow" +
+			(this.disabled ? "-disabled" : "") + ".gif");
+
+		// Attach menu to body so that it appears above other buttons
+		this.menu.style.position="absolute";
+		this.menu.style.display="none";
+		this.menu.style["z-index"] = 99;
+		document.body.appendChild(this.menu);
+	},
+
+	postCreate: function() {
+		if ( dojo.render.html.ie ) {
+			// Compensate for IE's weird padding of button content, which seems to be relative
+			// to the length of the content
+			var contentWidth = dojo.style.getOuterWidth(this.table);
+			this.labelCell.style["left"] = "-" + (contentWidth / 10) + "px";
+			this.arrowCell.style["left"] = (contentWidth / 10) + "px";
+		}
+
+		// Make menu at least as wide as the button
+		var buttonWidth = dojo.style.getOuterWidth(this.button);
+		var menuWidth = dojo.style.getOuterWidth(this.menu);
+		if ( buttonWidth > menuWidth ) {
+			dojo.style.setOuterWidth(this.menu, buttonWidth);
+		}
+	},
+
+	// If someone clicks anywhere else on the screen (including another menu),
+	// then close this menu.
+	onCanvasMouseDown: function(e) {
+		if( !dojo.dom.isDescendantOf(e.target, this.button) &&
+			!dojo.dom.isDescendantOf(e.target, this.menu) ) {
+			this.hideMenu();
+		}
+	},
+
+	eventWasOverArrow: function(e) {
+		// want to use dojo.html.overElement() but also need to detect clicks
+		// on the area between the arrow and the edge of the button
+		var eventX = e.clientX;
+		var borderX = dojo.style.totalOffsetLeft(this.borderCell);
+		return (eventX > borderX );
+	},
+
+	onMouseOver: function(e) {
+		dojo.html.addClass(this.button, "dojoButtonHover");
+		dojo.html.removeClass(this.button, "dojoButtonNoHover");
+	},
+	
+	onMouseOut: function(e) {
+		dojo.html.removeClass(this.button, "dojoButtonHover");
+		dojo.html.addClass(this.button, "dojoButtonNoHover");
+	},
+
+	onClick: function(e) {
+		if ( this.eventWasOverArrow(e) ) {
+			this._onClickArrow();
+		} else {
+			this._onClickButton();
+		}
+	},
+
+	// Action when the user presses the button
+	_onClickButton: function(e) {
+		if ( this.a ) {
+			if ( this.a.click ) {
+				this.a.click();
+			} else if ( this.a.href ) {
+				location.href = this.a.href;
+			}
+		}
+	},
+
+	// Action when user presses the arrow
+	_onClickArrow: function() {
+		if ( this.menu.style.display == "none" ) {
+			this.showMenu();
+		} else {
+			this.hideMenu();
+		}
+	},
+	
+	showMenu: function() {
+		if ( this.disabled )
+			return;
+
+		// Position it accordingly, relative to screen root (since
+		// it's attached to document.body)
+		this.menu.style.left = dojo.style.totalOffsetLeft(this.button) + "px";
+		this.menu.style.top = dojo.style.totalOffsetTop(this.button) + dojo.style.getOuterHeight(this.button) + "px";
+
+		// Display the menu; do this funky code below to stop the menu from extending
+		// all the way to the right edge of the screen.
+		// TODO: retest simple display="" to confirm that it doesn't work.
+		try {
+			this.menu.style.display="table";	// mozilla
+		} catch(e) {
+			this.menu.style.display="block";	// IE
+		}
+
+		// If someone clicks somewhere else on the screen then close the menu
+		dojo.event.connect(document.documentElement, "onmousedown", this, "onCanvasMouseDown");
+		
+		// When someone clicks the menu, after the menu handles the event,
+		// close the menu (be careful not to close the menu too early or else
+		// the menu will never receive the event.)
+		dojo.event.connect(this.menu, "onclick", this, "hideMenu");
+	},
+
+	hideMenu: function() {
+		this.menu.style.display = "none";
+		dojo.event.disconnect(document.documentElement, "onmousedown", this, "onCanvasMouseDown");
+		dojo.event.disconnect(this.menu, "onclick", this, "hideMenu");
+	}
+});
+
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LayoutPane.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LayoutPane.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LayoutPane.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LayoutPane.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,419 @@
+/*
+	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.LayoutPane");
+dojo.provide("dojo.widget.html.LayoutPane");
+dojo.require("dojo.string.extras");
+
+//
+// this widget provides Delphi-style panel layout semantics
+// this is a good place to stash layout logic, then derive components from it
+//
+// TODO: allow more edge priority orders (e.g. t,r,l,b)
+// TODO: allow percentage sizing stuff
+//
+
+dojo.require("dojo.widget.LayoutPane");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.string");
+
+
+dojo.widget.html.LayoutPane = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.LayoutPane, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.LayoutPane, {
+	widgetType: "LayoutPane",
+	isContainer: true,
+
+	isChild: false,
+
+	clientWidth: 0,
+	clientHeight: 0,
+
+	layoutChildPriority: 'top-bottom',
+
+	cssPath: dojo.uri.dojoUri("src/widget/templates/HtmlLayoutPane.css"),
+
+	// If this pane's content is external then set the url here	
+	url: "inline",
+	extractContent: true,
+	parseContent: true,
+	cacheContent: true,
+	
+	// To generate pane content from a java function
+	handler: "none",
+
+	minWidth: 0,
+	minHeight: 0,
+
+	fillInTemplate: function(){
+		this.filterAllowed(this, 'layoutChildPriority', ['left-right', 'top-bottom']);
+
+		// Need to include CSS manually because there is no template file/string
+		dojo.style.insertCssFile(this.cssPath, null, true);
+		dojo.html.addClass(this.domNode, "dojoLayoutPane");
+	},
+
+	postCreate: function(args, fragment, parentComp){
+		for(var i=0; i<this.children.length; i++){
+			this._injectChild(this.children[i]);
+		}
+
+		if ( this.handler != "none" ){
+			this.setHandler(this.handler);
+		}
+		if ( this.isVisible() ){
+			this.loadContents();
+		}
+	},
+
+	// If the pane contents are external then load them
+	loadContents: function() {
+		if ( this.isLoaded ){
+			return;
+		}
+		if ( dojo.lang.isFunction(this.handler)) {
+			this._runHandler();
+		} else if ( this.url != "inline" ) {
+			this._downloadExternalContent(this.url, this.cacheContent);
+		}
+		this.isLoaded=true;
+	},
+
+	// Reset the (external defined) content of this pane
+	setUrl: function(url) {
+		this.url = url;
+		this.isLoaded = false;
+		if ( this.isVisible() ){
+			this.loadContents();
+		}
+	},
+
+	_downloadExternalContent: function(url, useCache) {
+		dojo.deprecated("LayoutPane url parameter.", "use LinkPane to download from a URL", "0.4");
+		//dojo.debug(this.widgetId + " downloading " + url);
+		var node = this.containerNode || this.domNode;
+		node.innerHTML = "Loading...";
+
+		var extract = this.extractContent;
+		var parse = this.parseContent;
+		var self = this;
+
+		dojo.io.bind({
+			url: url,
+			useCache: useCache,
+			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;
+					if(parse) {
+						var parser = new dojo.xml.Parse();
+						var frag = parser.parseElement(node, null, true);
+						dojo.widget.getParser().createComponents(frag);
+					}
+					self.onResized();
+				} else {
+					node.innerHTML = "Error loading '" + url + "' (" + e.status + " " + e.statusText + ")";
+				}
+			}
+		});
+	},
+
+	// Generate pane content from given java function
+	setHandler: function(handler) {
+		var fcn = dojo.lang.isFunction(handler) ? handler : window[handler];
+		if(!dojo.lang.isFunction(fcn)) {
+			throw new Error("Unable to set handler, '" + handler + "' not a function.");
+			return;
+		}
+		this.handler = function() {
+			return fcn.apply(this, arguments);
+		}
+	},
+
+	_runHandler: function() {
+		if(dojo.lang.isFunction(this.handler)) {
+			dojo.deprecated("use LinkPane to download content from a java function", "0.4");
+			this.handler(this, this.domNode);
+			return false;
+		}
+		return true;
+	},
+
+	filterAllowed: function(node, param, values){
+		if ( !dojo.lang.inArray(values, node[param]) ) {
+			node[param] = values[0];
+		}
+	},
+
+	layoutChildren: function(){
+		// find the children to arrange
+
+		var kids = {'left':[], 'right':[], 'top':[], 'bottom':[], 'client':[], 'flood':[]};
+		var hits = 0;
+
+		for(var i=0; i<this.children.length; i++){
+			if (this.hasLayoutAlign(this.children[i])){
+				kids[this.children[i].layoutAlign].push(this.children[i]);
+				hits++;
+			}
+		}
+
+		if (!hits){
+			return;
+		}
+
+		var container = this.containerNode || this.domNode;
+
+		// calc layout space
+
+		this.clientWidth  = dojo.style.getContentWidth(container);
+		this.clientHeight = dojo.style.getContentHeight(container);
+
+		// note: don't setup clientRect as a member of the prototype because that
+		// would make the contents shared between instances
+		this.clientRect={};
+		this.clientRect['left']   = dojo.style.getPixelValue(container, "padding-left", true);
+		this.clientRect['right']  = dojo.style.getPixelValue(container, "padding-right", true);
+		this.clientRect['top']    = dojo.style.getPixelValue(container, "padding-top", true);
+		this.clientRect['bottom'] = dojo.style.getPixelValue(container, "padding-bottom", true);
+
+		// arrange them in order
+		this._layoutCenter(kids, "flood");
+		if (this.layoutChildPriority == 'top-bottom'){
+			this._layoutFloat(kids, "top");
+			this._layoutFloat(kids, "bottom");
+			this._layoutFloat(kids, "left");
+			this._layoutFloat(kids, "right");
+		}else{
+			this._layoutFloat(kids, "left");
+			this._layoutFloat(kids, "right");
+			this._layoutFloat(kids, "top");
+			this._layoutFloat(kids, "bottom");
+		}
+		this._layoutCenter(kids, "client");
+	},
+
+	// Position the left/right/top/bottom aligned elements
+	_layoutFloat: function(kids, position){
+		var ary = kids[position];
+		
+		// figure out which two of the left/right/top/bottom properties to set
+		var lr = (position=="right")?"right":"left";
+		var tb = (position=="bottom")?"bottom":"top";
+
+		for(var i=0; i<ary.length; i++){
+			var elm=ary[i];
+			
+			// set two of left/right/top/bottom properties
+			elm.domNode.style[lr]=this.clientRect[lr] + "px";
+			elm.domNode.style[tb]=this.clientRect[tb] + "px";
+			
+			// adjust record of remaining space
+			if ( (position=="top")||(position=="bottom") ) {
+				dojo.style.setOuterWidth(elm.domNode, this.clientWidth);
+				var height = dojo.style.getOuterHeight(elm.domNode);
+				this.clientHeight -= height;
+				this.clientRect[position] += height;
+			} else {
+				dojo.style.setOuterHeight(elm.domNode, this.clientHeight);
+				var width = dojo.style.getOuterWidth(elm.domNode);
+				this.clientWidth -= width;
+				this.clientRect[position] += width;
+			}
+		}
+	},
+
+	// Position elements into the remaining space (in the center)
+	// If multiple elements are present they overlap each other
+	_layoutCenter: function(kids, position){
+		var ary = kids[position];
+		for(var i=0; i<ary.length; i++){
+			var elm=ary[i];
+			elm.domNode.style.left=this.clientRect.left + "px";
+			elm.domNode.style.top=this.clientRect.top + "px";
+			dojo.style.setOuterWidth(elm.domNode, this.clientWidth);		
+			dojo.style.setOuterHeight(elm.domNode, this.clientHeight);
+		}
+
+	},
+
+	hasLayoutAlign: function(child){
+		return dojo.lang.inArray(['left','right','top','bottom','client', 'flood'], child.layoutAlign);
+	},
+
+	addChild: function(child, overrideContainerNode, pos, ref, insertIndex){
+		this._injectChild(child);
+		dojo.widget.html.LayoutPane.superclass.addChild.call(this, child, overrideContainerNode, pos, ref, insertIndex);
+		this.resizeSoon();
+	},
+
+	_injectChild: function(child){
+		if ( this.hasLayoutAlign(child) ){
+			child.domNode.style.position = 'absolute';
+			child.isChild = true;	
+			this.filterAllowed(child, 'layoutAlign', ['none', 'left', 'top', 'right', 'bottom', 'client', 'flood']);
+			dojo.html.addClass(child.domNode, "dojoAlign" + dojo.string.capitalize(child.layoutAlign));		
+		}
+	},
+
+	removeChild: function(pane){
+		dojo.widget.html.LayoutPane.superclass.removeChild.call(this,pane);
+		dojo.dom.removeNode(pane.domNode);
+		this.resizeSoon();
+	},
+
+	onResized: function(){
+		if ( !this.isVisible() ) {
+			return;
+		}
+
+		//dojo.debug(this.widgetId + ": resized");
+
+		// set position/size for my children
+		this.layoutChildren();
+
+		// notify children that they have been moved/resized
+		this.notifyChildrenOfResize();
+	},
+
+	resizeTo: function(w, h){
+
+		w = Math.max(w, this.getMinWidth());
+		h = Math.max(h, this.getMinHeight());
+
+		dojo.style.setOuterWidth(this.domNode, w);
+		dojo.style.setOuterHeight(this.domNode, h);
+		this.onResized();
+	},
+
+	show: function(){
+		// If this is the first time we are displaying this object,
+		// and the contents are external, then download them.
+		this.loadContents();
+
+		// If this node was created while display=="none" then it
+		// hasn't been laid out yet.  Do that now.
+		this.domNode.style.display="";
+		this.onResized();
+		this.domNode.style.display="none";
+		this.domNode.style.visibility="";
+
+		dojo.widget.html.LayoutPane.superclass.show.call(this);
+	},
+
+	getMinWidth: function(){
+
+		//
+		// we need to first get the cumulative width
+		//
+
+		var w = this.minWidth;
+
+		if ((this.layoutAlign == 'left') || (this.layoutAlign == 'right')){
+
+			w = dojo.style.getOuterWidth(this.domNode);
+		}
+
+		for(var i=0; i<this.children.length; i++){
+			var ch = this.children[i];
+			var a = ch.layoutAlign;
+
+			if ((a == 'left') || (a == 'right') || (a == 'client')){
+
+				if (dojo.lang.isFunction(ch.getMinWidth)){
+					w += ch.getMinWidth();
+				}
+			}
+		}
+
+		//
+		// but then we need to check to see if the top/bottom kids are larger
+		//
+
+		for(var i=0; i<this.children.length; i++){
+			var ch = this.children[i];
+			var a = ch.layoutAlign;
+
+			if ((a == 'top') || (a == 'bottom')){
+
+				if (dojo.lang.isFunction(ch.getMinWidth)){
+					w = Math.max(w, ch.getMinWidth());
+				}
+			}
+		}
+
+		return w;
+	},
+
+	getMinHeight: function(){
+
+		//
+		// we need to first get the cumulative height
+		//
+
+		var h = this.minHeight;
+
+		if ((this.layoutAlign == 'top') || (this.layoutAlign == 'bottom')){
+
+			h = dojo.style.getOuterHeight(this.domNode);
+		}
+
+		for(var i=0; i<this.children.length; i++){
+			var ch = this.children[i];
+			var a = ch.layoutAlign;
+
+			if ((a == 'top') || (a == 'bottom') || (a == 'client')){
+
+				if (dojo.lang.isFunction(ch.getMinHeight)){
+					h += ch.getMinHeight();
+				}
+			}
+		}
+
+		//
+		// but then we need to check to see if the left/right kids are larger
+		//
+
+		for(var i=0; i<this.children.length; i++){
+			var ch = this.children[i];
+			var a = ch.layoutAlign;
+
+			if ((a == 'left') || (a == 'right')){
+
+				if (dojo.lang.isFunction(ch.getMinHeight)){
+					h = Math.max(h, ch.getMinHeight());
+				}
+			}
+		}
+
+		return h;
+	}
+});
+
+// This arguments can be specified for the children of a LayoutPane.
+// Since any widget can be specified as a LayoutPane child, mix it
+// into the base widget class.  (This is a hack, but it's effective.)
+dojo.lang.extend(dojo.widget.Widget, {
+	layoutAlign: 'none'
+});
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LinkPane.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LinkPane.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LinkPane.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/LinkPane.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,55 @@
+/*
+	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.LinkPane");
+dojo.provide("dojo.widget.html.LinkPane");
+
+//
+// a div that loads from a URL.  (Similar to an iframe, but
+// it's in the same environment as the main window)
+//
+
+dojo.require("dojo.widget.LinkPane");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.*");
+dojo.require("dojo.widget.ContentPane");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.string");
+
+
+dojo.widget.html.LinkPane = function(){
+	dojo.widget.html.ContentPane.call(this);
+}
+
+dojo.inherits(dojo.widget.html.LinkPane, dojo.widget.html.ContentPane);
+
+dojo.lang.extend(dojo.widget.html.LinkPane, {
+	widgetType: "LinkPane",
+
+	// I'm using a template because the user may specify the input as
+	// <a href="foo.html">label</a>, in which case we need to get rid of the
+	// <a> because we don't want a link.
+	templateString: '<div class="dojoLinkPane"></div>',
+
+	fillInTemplate: function(args, frag){
+		var source = this.getFragNodeRef(frag);
+
+		// If user has specified node contents, they become the label
+		// (the link must be plain text)
+		this.label += source.innerHTML;
+
+		// Copy style info from input node to output node
+		this.domNode.style.cssText = source.style.cssText;
+		dojo.html.addClass(this.domNode, dojo.html.getClass(source));
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Menu.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Menu.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Menu.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/Menu.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,51 @@
+/*
+	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.Menu");
+dojo.provide("dojo.widget.html.Menu");
+
+/* HtmlMenu
+ ***********/
+ 
+dojo.widget.html.Menu = function(){
+	dojo.widget.html.Menu.superclass.constructor.call(this);
+	this.items = [];
+}
+dojo.inherits(dojo.widget.html.Menu, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.Menu, {
+	widgetType: "Menu",
+	isContainer: true,
+
+	// copy children widgets output directly to parent (this node), to avoid
+	// errors trying to insert an <li> under a <div>
+	snarfChildDomOutput: true,
+
+	templateString: '<ul></ul>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/Menu.css"),
+	
+	fillInTemplate: function (args, frag){
+		//dojo.widget.HtmlMenu.superclass.fillInTemplate.apply(this, arguments);
+		this.domNode.className = "dojoMenu";
+	},
+	
+ 
+	_register: function (item ) {
+		dojo.event.connect(item, "onSelect", this, "onSelect");
+		this.items.push(item);
+	},
+
+	push: function (item) {
+		this.domNode.appendChild(item.domNode);
+		this._register(item);
+	}
+
+});
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MenuItem.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MenuItem.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MenuItem.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MenuItem.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,60 @@
+/*
+	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.html.MenuItem");
+
+/* HtmlMenuItem
+ ***************/
+
+dojo.widget.html.MenuItem = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.MenuItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.MenuItem, {
+	widgetType: "MenuItem",
+	templateString: '<li class="dojoMenuItem" dojoAttachEvent="onMouseOver; onMouseOut; onMouseDown; onMouseUp; onClick;"></li>',
+	title: "",
+
+	fillInTemplate: function(args, frag){
+		dojo.html.disableSelection(this.domNode);
+
+		if(!dojo.string.isBlank(this.title)){
+			this.domNode.appendChild(document.createTextNode(this.title));
+		}else{
+			this.domNode.appendChild(frag["dojo:"+this.widgetType.toLowerCase()]["nodeRef"]);
+		}
+	},
+	
+	onMouseOver: function(e){
+		dojo.html.addClass(this.domNode, "dojoMenuItemHover");
+	},
+	
+	onMouseOut: function(e){
+		dojo.html.removeClass(this.domNode, "dojoMenuItemHover");
+	},
+	
+	onClick: function(e){ this.onSelect(this, e); },
+	onMouseDown: function(e){},
+	onMouseUp: function(e){},
+	
+	// By default, when I am clicked, click the item inside of me
+	onSelect: function (item, e) {
+		var child = dojo.dom.getFirstChildElement(this.domNode);
+		if(child){
+			if(child.click){
+				child.click();
+			}else if(child.href){
+				location.href = child.href;
+			}
+		}
+	}
+});
+

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MonthlyCalendar.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MonthlyCalendar.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MonthlyCalendar.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/MonthlyCalendar.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,149 @@
+/*
+	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.html.MonthlyCalendar");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.html.DatePicker");
+dojo.require("dojo.widget.MonthlyCalendar");
+//dojo.require("dojo.widget.MonthlyCalendar.util");
+dojo.require("dojo.event.*");
+dojo.require("dojo.html");
+
+dojo.widget.html.MonthlyCalendar= function(){
+	dojo.widget.MonthlyCalendar.call(this);
+	//dojo.widget.html.DatePicker.call(this);
+	this.widgetType = "MonthlyCalendar";
+	this.templatePath =  dojo.uri.dojoUri("src/widget/templates/HtmlMonthlyCalendar.html");
+	this.templateCssPath = dojo.uri.dojoUri("src/widget/templates/HtmlMonthlyCalendar.css");
+
+	this.iCalendars = [];
+}
+
+dojo.inherits(dojo.widget.html.MonthlyCalendar, dojo.widget.html.DatePicker);
+
+dojo.lang.extend(dojo.widget.html.MonthlyCalendar, {
+	cache: function() {
+	},
+
+	addCalendar: function(/* dojo.iCalendar */ cal) {
+		dojo.debug("Adding Calendar");
+		this.iCalendars.push(cal);
+		this.initUI();
+	},
+
+	createDayContents: function(node,mydate) {
+		dojo.dom.removeChildren(node);
+		node.appendChild(document.createTextNode(mydate.getDate()));	
+		if (this.cache[mydate]) {
+			evts = this.cache[mydate];
+			if ((dojo.lang.isArray(evts)) && (evts.length>0)) {
+				for(var y=0;y<evts.length;y++) {
+					var el = document.createElement("div");
+					dojo.html.addClass(el, "dojoMonthlyCalendarEvent");          
+					el.appendChild(document.createTextNode(evts[y].summary.value));
+					el.width = dojo.style.getContentWidth(node);
+					node.appendChild(el);
+				}
+			}
+		} else {
+			for(var x=0; x<this.iCalendars.length; x++) {
+				evts = this.iCalendars[x].getEvents(mydate);
+
+				if ((dojo.lang.isArray(evts)) && (evts.length>0)) {
+					this.cache[mydate]=evts;
+					for(var y=0;y<evts.length;y++) {
+						var el = document.createElement("div");
+						dojo.html.addClass(el, "dojoMonthlyCalendarEvent");          
+						el.appendChild(document.createTextNode(evts[y].summary.value));
+						el.width = dojo.style.getContentWidth(node);
+						node.appendChild(el);
+					}
+				} else {
+					this.cache[mydate]=[];
+				}
+			}
+		}
+	},
+
+	initUI: function() {
+		this.selectedIsUsed = false;
+		this.currentIsUsed = false;
+		var currentClassName = "";
+		var previousDate = new Date();
+		var calendarNodes = this.calendarDatesContainerNode.getElementsByTagName("td");
+		var currentCalendarNode;
+		// set hours of date such that there is no chance of rounding error due to 
+		// time change in local time zones
+		previousDate.setHours(8);
+		var nextDate = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date, 8);
+		var lastDay = new Date(this.firstSaturday.year, this.firstSaturday.month, this.firstSaturday.date + 42, 8);
+		
+		if (this.iCalendars.length > 0) {
+			for (var x=0; x<this.iCalendars.length;x++) {
+				this.iCalendars[x].preComputeRecurringEvents(lastDay);
+			}
+		}
+
+		if(this.firstSaturday.date < 7) {
+			// this means there are days to show from the previous month
+			var dayInWeek = 6;
+			for (var i=this.firstSaturday.date; i>0; i--) {
+				currentCalendarNode = calendarNodes.item(dayInWeek);
+				this.createDayContents(currentCalendarNode, nextDate);
+				
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+				dayInWeek--;
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, false);
+			}
+			for(var i=dayInWeek; i>-1; i--) {
+				currentCalendarNode = calendarNodes.item(i);
+
+				this.createDayContents(currentCalendarNode, nextDate);
+
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "previous"));
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, false);				
+			}
+		} else {
+			nextDate.setDate(1);
+			for(var i=0; i<7; i++) {
+				currentCalendarNode = calendarNodes.item(i);
+				this.createDayContents(currentCalendarNode, nextDate);
+				dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+				previousDate = nextDate;
+				nextDate = this.incrementDate(nextDate, true);				
+			}
+		}
+		previousDate.setDate(this.firstSaturday.date);
+		previousDate.setMonth(this.firstSaturday.month);
+		previousDate.setFullYear(this.firstSaturday.year);
+		nextDate = this.incrementDate(previousDate, true);
+		var count = 7;
+		currentCalendarNode = calendarNodes.item(count);
+		while((nextDate.getMonth() == previousDate.getMonth()) && (count<42)) {
+			this.createDayContents(currentCalendarNode, nextDate);
+			dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "current"));
+			currentCalendarNode = calendarNodes.item(++count);
+			previousDate = nextDate;
+			nextDate = this.incrementDate(nextDate, true);
+		}
+		
+		while(count < 42) {
+			this.createDayContents(currentCalendarNode, nextDate);
+			dojo.html.setClass(currentCalendarNode, this.getDateClassName(nextDate, "next"));
+			currentCalendarNode = calendarNodes.item(++count);
+			previousDate = nextDate;
+			nextDate = this.incrementDate(nextDate, true);
+		}
+		this.setMonthLabel(this.firstSaturday.month);
+		this.setYearLabels(this.firstSaturday.year);
+	}	
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/SortableTable.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/SortableTable.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/SortableTable.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/SortableTable.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,323 @@
+/*
+	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.html.SortableTable");
+dojo.require("dojo.lang");
+dojo.require("dojo.date");
+dojo.require("dojo.html");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.widget.SortableTable");
+
+dojo.widget.html.SortableTable=function(){
+	//	summary
+	//	Constructor for the SortableTable widget
+	dojo.widget.SortableTable.call(this);
+	dojo.widget.HtmlWidget.call(this);
+
+	this.containerClass="";
+	this.headClass="";
+	this.tbodyClass="";
+	this.headerClass="";
+	this.headerSortUpClass="selected";
+	this.headerSortDownClass="selected";
+	this.rowClass="";
+	this.rowAlternateClass="alt";
+	this.rowSelectedClass="selected";
+};
+dojo.inherits(dojo.widget.html.SortableTable, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.SortableTable, {
+	templatePath:null,
+	templateCssPath:null,
+
+	getTypeFromString:function(/* string */ s){
+		//	summary
+		//	Find the constructor that matches param s by searching through the entire object tree.
+		var parts=s.split("."),i=0,obj=dj_global; 
+		do{obj=obj[parts[i++]];}while(i<parts.length&&obj); 
+		return(obj!=dj_global)?obj:null;	//	function
+	},
+	compare:function(/* Object */ o1, /* Object */ o2){
+		//	summary
+		//	Compare two objects using a shallow property compare
+		for(var p in o1){
+			if(!o2[p]) return false;	//	boolean
+			if(o1[p].valueOf()!=o2[p].valueOf()) return false;	//	boolean
+		}
+		return true;	// boolean
+	},
+
+	getSelection:function(){
+		//	summary
+		//	return the currently selected object (JSON format)
+		return this.selected;	//	object
+	},
+	parseData:function(/* Object */ data){
+		//	summary
+		//	Parse the passed JSON data structure, and cast based on columns.
+		this.data=[];
+		for(var i=0; i<data.length; i++){
+			var o={};	//	new data object.
+			for(var j=0; j<this.columns.length; j++){
+				var field=this.columns[j].getField();
+				var type=this.columns[j].getType();
+				var val=data[i][field];
+				var t=this.columns[j].sortType.toLowerCase();
+				if(val)
+					o[field]=new type(val);
+				else
+					o[field]=new type();	//	let it use the default.
+			}
+			this.data.push(o);
+		}
+	}, 
+
+	getObjectFromRow:function(/* HTMLTableRowElement */ row){
+		//	summary
+		//	creates a JSON object based on the passed row
+		var cells=row.getElementsByTagName("td");
+		var o={};
+		for(var i=0; i<this.columns.length;i++){
+			var text=dojo.html.renderedTextContent(cells[i]);
+			var val=new (this.columns[i].getType())(text);
+			o[this.columns[i].getField()]=val;
+		}
+		return o;	//	object
+	},
+	setSelectionByRow:function(/* HTMLTableElementRow */ row){
+		//	summary
+		//	create the selection object based on the passed row
+		this.selected=this.getObjectFromRow(row);
+		var body=dojo.html.getParentByType(row,"tbody");
+		if(body){
+			var rows=body.getElementsByTagName("tr");
+			for(var i=0; i<rows.length; i++){
+				if(rows[i].className==this.rowSelectedClass){
+					if(this.enableAlternateRows&&i%2==1){
+						rows[i].className=this.rowAlternateClass;
+					}else rows[i].className="";
+				}
+			}
+			row.className=this.rowSelectedClass;
+		}
+	},
+
+	parseColumns:function(/* HTMLTableHeadElement */ node){
+		//	summary
+		//	parses the passed element to create column objects
+		this.columns=[];	//	reset it.
+		var row=node.getElementsByTagName("tr")[0];
+		var cells=row.getElementsByTagName("td");
+		if (cells.length==0) cells=row.getElementsByTagName("th");
+		for(var i=0; i<cells.length; i++){
+			var o={
+				field:null,
+				format:null,
+				sortType:"String",
+				dataType:String,
+				label:null,
+				getField:function(){ return this.field||this.label; },
+				getType:function(){ return this.dataType; }
+			};
+			if(dojo.html.hasAttribute(cells[i], "field")){
+				o.field=dojo.html.getAttribute(cells[i],"field");
+			}
+			if(dojo.html.hasAttribute(cells[i], "format")){
+				o.format=dojo.html.getAttribute(cells[i],"format");
+			}
+			if(dojo.html.hasAttribute(cells[i], "dataType")){
+				var sortType=dojo.html.getAttribute(cells[i],"dataType");
+				var type=this.getTypeFromString(sortType);
+				if(type){
+					o.sortType=sortType;
+					o.dataType=type;
+				}
+			}
+			o.label=dojo.html.renderedTextContent(cells[i]);
+			this.columns.push(o);
+		}
+	},
+
+	parseDataFromTable:function(/* HTMLTableBodyElement */ tbody){
+		//	summary
+		//	parses the data in the tbody of a table to create a set of objects
+		this.data=[];
+		var rows=tbody.getElementsByTagName("tr");
+		for(var i=0; i<rows.length; i++){
+			var o={};	//	new data object.
+			var cells=rows[i].getElementsByTagName("td");
+			for(var j=0; j<this.columns.length; j++){
+				var field=this.columns[j].getField();
+				var type=this.columns[j].getType();
+				var val=dojo.html.renderedTextContent(cells[j]); //	should be the same index as the column.
+				if (val) o[field]=new type(val);
+				else o[field]=new type();	//	let it use the default.
+			}
+			this.data.push(o);
+		}
+	},
+	
+	render:function(){
+		//	summary
+		//	renders the table to the browser
+		var data=[];
+		var body=this.domNode.getElementsByTagName("tbody")[0];
+		for(var i=0; i<this.data.length; i++){
+			data.push(this.data[i]);
+		}
+		var col=this.columns[this.sortIndex];
+		var field=col.getField();
+		if (this.sortFunctions[col.sortType]) 
+			var sort=this.sortFunctions[col.sortType];
+		else{
+			var sort=function(a,b){
+				if (a[field]>b[field]) return 1;
+				if (a[field]<b[field]) return -1;
+				return 0;
+			}
+		}
+		data.sort(sort);
+		if(this.sortDirection!=0) data.reverse();
+
+		//	build the table and pop it in.
+		//	IE doesn't like using innerHTML, so we're stuck with the DOM.
+		//	a little slower, but works just the same :)
+		while(body.childNodes.length>0) body.removeChild(body.childNodes[0]);
+		for(var i=0; i<data.length;i++){
+			var row=document.createElement("tr");
+			if(this.selected&&this.compare(this.selected,data[i])){
+				row.className=this.rowSelectedClass;
+			} else {
+				if(this.enableAlternateRows&&i%2==1){
+					row.className=this.rowAlternateClass;
+				}
+			}
+			for(var j=0;j<this.columns.length;j++){
+				var cell=document.createElement("td");
+				if(this.columns[j].getType()==Date){
+					var format=this.defaultDateFormat;
+					if(this.columns[j].format) format=this.columns[j].format;
+					cell.appendChild(document.createTextNode(dojo.date.format(data[i][this.columns[j].getField()], format)));
+				}else{
+					cell.appendChild(document.createTextNode(data[i][this.columns[j].getField()]));
+				}
+				row.appendChild(cell);
+			}
+			body.appendChild(row);
+			dojo.event.connect(row, "onclick", this, "onUISelect");
+		}
+		
+		//	if minRows exist.
+		var minRows=parseInt(this.minRows);
+		if (!isNaN(minRows) && minRows>0 && data.length<minRows){
+			var mod=0;
+			if(data.length%2==0) mod=1;
+			var nRows=minRows-data.length;
+			for(var i=0; i<nRows; i++){
+				var row=document.createElement("tr");
+				if(this.enableAlternateRows&&i%2==mod){
+					row.className=this.rowAlternateClass;
+				}
+				for(var j=0;j<this.columns.length;j++){
+					var cell=document.createElement("td");
+					cell.appendChild(document.createTextNode("\u00A0"));
+					row.appendChild(cell);
+				}
+				body.appendChild(row);
+			}
+		}
+	},
+
+	//	the following the user can override.
+	onSelect:function(/* DomEvent */ e){ 
+		//	summary
+		//	empty function for the user to attach code to, fired by onUISelect
+	},
+	onUISelect:function(/* DomEvent */ e){
+		//	summary
+		//	fired when a user selects a row
+		var row=dojo.html.getParentByType(e.target,"tr");
+		this.setSelectionByRow(row);
+		this.onSelect(e);
+	},
+	onHeaderClick:function(/* DomEvent */ e){
+		//	summary
+		//	Main handler function for each header column click.
+		var oldIndex=this.sortIndex;
+		var oldDirection=this.sortDirection;
+		var source=e.target;
+		var row=dojo.html.getParentByType(source,"tr");
+		var cellTag="td";
+		if(row.getElementsByTagName(cellTag).length==0) cellTag="th";
+
+		var headers=row.getElementsByTagName(cellTag);
+		var header=dojo.html.getParentByType(source,cellTag);
+		
+		for(var i=0; i<headers.length; i++){
+			if(headers[i]==header){
+				if(i!=oldIndex){
+					//	new col.
+					this.sortIndex=i;
+					this.sortDirection=0;
+					headers[i].className=this.headerSortDownClass
+				}else{
+					this.sortDirection=(oldDirection==0)?1:0;
+					if(this.sortDirection==0){
+						headers[i].className=this.headerSortDownClass;
+					}else{
+						headers[i].className=this.headerSortUpClass;
+					}
+				}
+			}else{
+				//	reset the header class.
+				headers[i].className=this.headerClass;
+			}
+		}
+		this.render();
+	},
+
+	postCreate:function(){ 
+		// 	summary
+		//	overridden from HtmlWidget, initializes and renders the widget.
+		var div=document.createElement("div");
+		if(this.containerClass.length>0){
+			div.className=this.containerClass;
+		}
+		var p=this.domNode.parentNode;
+		div.appendChild(this.domNode);
+		p.appendChild(div);
+		
+		var thead=this.domNode.getElementsByTagName("thead")[0];
+		if(this.headClass.length>0){
+			thead.className=this.headClass;
+		}
+
+		//	parse the columns.
+		this.parseColumns(thead);
+
+		//	attach header handlers.
+		var header="td";
+		if(thead.getElementsByTagName(header).length==0) header="th";
+		var headers=thead.getElementsByTagName(header);
+		for(var i=0; i<headers.length; i++){
+			dojo.event.connect(headers[i], "onclick", this, "onHeaderClick");
+		}
+
+		//	parse the tbody element and re-render it.
+		var tbody=this.domNode.getElementsByTagName("tbody")[0];
+		if (this.tbodyClass.length>0) {
+			tbody.className=this.tbodyClass;
+		}
+
+		this.parseDataFromTable(tbody);
+		this.render();
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/TaskBar.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/TaskBar.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/TaskBar.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/html/TaskBar.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,74 @@
+/*
+	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.html.TaskBar");
+dojo.provide("dojo.widget.html.TaskBarItem");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.FloatingPane");
+dojo.require("dojo.widget.HtmlWidget");
+dojo.require("dojo.event");
+
+// Icon associated w/a floating pane
+dojo.widget.html.TaskBarItem = function(){
+	dojo.widget.TaskBarItem.call(this);
+	dojo.widget.HtmlWidget.call(this);
+}
+dojo.inherits(dojo.widget.html.TaskBarItem, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.TaskBarItem, {
+	// constructor arguments
+	iconSrc: '',
+	caption: 'Untitled',
+	window: null,
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlTaskBarItemTemplate.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlTaskBar.css"),
+
+	fillInTemplate: function() {
+		if ( this.iconSrc != '' ) {
+			var img = document.createElement("img");
+			img.src = this.iconSrc;
+			this.domNode.appendChild(img);
+		}
+		this.domNode.appendChild(document.createTextNode(this.caption));
+		dojo.html.disableSelection(this.domNode);
+	},
+
+	postCreate: function() {
+		this.window=dojo.widget.getWidgetById(this.windowId);
+		this.window.explodeSrc = this.domNode;
+		dojo.event.connect(this.window, "destroy", this, "destroy")
+	},
+
+	onClick: function() {
+		if (this.window.windowState != "minimized") {
+			this.window.bringToTop();
+		} else {
+			this.window.restoreWindow();
+		}
+	}
+});
+
+// Collection of widgets in a bar, like Windows task bar
+dojo.widget.html.TaskBar = function(){
+
+	dojo.widget.html.FloatingPane.call(this);
+	dojo.widget.TaskBar.call(this);
+	this.titleBarDisplay = "none";
+}
+
+dojo.inherits(dojo.widget.html.TaskBar, dojo.widget.html.FloatingPane);
+
+dojo.lang.extend(dojo.widget.html.TaskBar, {
+	addChild: function(child) {
+		var tbi = dojo.widget.createWidget("TaskBarItem",{windowId:child.widgetId, caption: child.title, iconSrc: child.iconSrc} );
+		dojo.widget.html.TaskBar.superclass.addChild.call(this,tbi);
+	}
+});



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