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 [16/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/Manager.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Manager.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Manager.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Manager.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,292 @@
+/*
+	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.Manager");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.event.*");
+
+// Manager class
+dojo.widget.manager = new function(){
+	this.widgets = [];
+	this.widgetIds = [];
+	
+	// map of widgetId-->widget for widgets without parents (top level widgets)
+	this.topWidgets = {};
+
+	var widgetTypeCtr = {};
+	var renderPrefixCache = [];
+
+	this.getUniqueId = function (widgetType) {
+		return widgetType + "_" + (widgetTypeCtr[widgetType] != undefined ?
+			++widgetTypeCtr[widgetType] : widgetTypeCtr[widgetType] = 0);
+	}
+
+	this.add = function(widget){
+		dojo.profile.start("dojo.widget.manager.add");
+		this.widgets.push(widget);
+		// FIXME: the rest of this method is very slow!
+		if(widget.widgetId == ""){
+			if(widget["id"]){
+				widget.widgetId = widget["id"];
+			}else if(widget.extraArgs["id"]){
+				widget.widgetId = widget.extraArgs["id"];
+			}else{
+				widget.widgetId = this.getUniqueId(widget.widgetType);
+			}
+		}
+		if(this.widgetIds[widget.widgetId]){
+			dojo.debug("widget ID collision on ID: "+widget.widgetId);
+		}
+		this.widgetIds[widget.widgetId] = widget;
+		// Widget.destroy already calls removeById(), so we don't need to
+		// connect() it here
+		dojo.profile.end("dojo.widget.manager.add");
+	}
+
+	this.destroyAll = function(){
+		for(var x=this.widgets.length-1; x>=0; x--){
+			try{
+				// this.widgets[x].destroyChildren();
+				this.widgets[x].destroy(true);
+				delete this.widgets[x];
+			}catch(e){ }
+		}
+	}
+
+	// FIXME: we should never allow removal of the root widget until all others
+	// are removed!
+	this.remove = function(widgetIndex){
+		var tw = this.widgets[widgetIndex].widgetId;
+		delete this.widgetIds[tw];
+		this.widgets.splice(widgetIndex, 1);
+	}
+	
+	// FIXME: suboptimal performance
+	this.removeById = function(id) {
+		for (var i=0; i<this.widgets.length; i++){
+			if(this.widgets[i].widgetId == id){
+				this.remove(i);
+				break;
+			}
+		}
+	}
+
+	this.getWidgetById = function(id){
+		return this.widgetIds[id];
+	}
+
+	this.getWidgetsByType = function(type){
+		var lt = type.toLowerCase();
+		var ret = [];
+		dojo.lang.forEach(this.widgets, function(x){
+			if(x.widgetType.toLowerCase() == lt){
+				ret.push(x);
+			}
+		});
+		return ret;
+	}
+
+	this.getWidgetsOfType = function (id) {
+		dj_deprecated("getWidgetsOfType is depecrecated, use getWidgetsByType");
+		return dojo.widget.manager.getWidgetsByType(id);
+	}
+
+	this.getWidgetsByFilter = function(unaryFunc){
+		var ret = [];
+		dojo.lang.forEach(this.widgets, function(x){
+			if(unaryFunc(x)){
+				ret.push(x);
+			}
+		});
+		return ret;
+	}
+
+	this.getAllWidgets = function() {
+		return this.widgets.concat();
+	}
+
+	//	added, trt 2006-01-20
+	this.getWidgetByNode = function(/* DOMNode */ node){
+		var w=this.getAllWidgets();
+		for (var i=0; i<w.length; i++){
+			if (w[i].domNode==node){
+				return w[i];
+			}
+		}
+		return null;
+	}
+
+	// shortcuts, baby
+	this.byId = this.getWidgetById;
+	this.byType = this.getWidgetsByType;
+	this.byFilter = this.getWidgetsByFilter;
+	this.byNode = this.getWidgetByNode;
+
+	// map of previousally discovered implementation names to constructors
+	var knownWidgetImplementations = {};
+
+	// support manually registered widget packages
+	var widgetPackages = ["dojo.widget"];
+	for (var i=0; i<widgetPackages.length; i++) {
+		// convenience for checking if a package exists (reverse lookup)
+		widgetPackages[widgetPackages[i]] = true;
+	}
+
+	this.registerWidgetPackage = function(pname) {
+		if(!widgetPackages[pname]){
+			widgetPackages[pname] = true;
+			widgetPackages.push(pname);
+		}
+	}
+	
+	this.getWidgetPackageList = function() {
+		return dojo.lang.map(widgetPackages, function(elt) { return(elt!==true ? elt : undefined); });
+	}
+	
+	this.getImplementation = function(widgetName, ctorObject, mixins){
+		// try and find a name for the widget
+		var impl = this.getImplementationName(widgetName);
+		if(impl){ 
+			// var tic = new Date();
+			var ret = new impl(ctorObject);
+			// dojo.debug(new Date() - tic);
+			return ret;
+		}
+	}
+
+	this.getImplementationName = function(widgetName){
+		/*
+		 * This is the overly-simplistic implemention of getImplementation (har
+		 * har). In the future, we are going to want something that allows more
+		 * freedom of expression WRT to specifying different specializations of
+		 * a widget.
+		 *
+		 * Additionally, this implementation treats widget names as case
+		 * insensitive, which does not necessarialy mesh with the markup which
+		 * can construct a widget.
+		 */
+
+		var lowerCaseWidgetName = widgetName.toLowerCase();
+
+		var impl = knownWidgetImplementations[lowerCaseWidgetName];
+		if(impl){
+			return impl;
+		}
+
+		// first store a list of the render prefixes we are capable of rendering
+		if(!renderPrefixCache.length){
+			for(var renderer in dojo.render){
+				if(dojo.render[renderer]["capable"] === true){
+					var prefixes = dojo.render[renderer].prefixes;
+					for(var i = 0; i < prefixes.length; i++){
+						renderPrefixCache.push(prefixes[i].toLowerCase());
+					}
+				}
+			}
+			// make sure we don't HAVE to prefix widget implementation names
+			// with anything to get them to render
+			renderPrefixCache.push("");
+		}
+
+		// look for a rendering-context specific version of our widget name
+		for(var i = 0; i < widgetPackages.length; i++){
+			var widgetPackage = dojo.evalObjPath(widgetPackages[i]);
+			if(!widgetPackage) { continue; }
+
+			for (var j = 0; j < renderPrefixCache.length; j++) {
+				if (!widgetPackage[renderPrefixCache[j]]) { continue; }
+				for (var widgetClass in widgetPackage[renderPrefixCache[j]]) {
+					if (widgetClass.toLowerCase() != lowerCaseWidgetName) { continue; }
+					knownWidgetImplementations[lowerCaseWidgetName] =
+						widgetPackage[renderPrefixCache[j]][widgetClass];
+					return knownWidgetImplementations[lowerCaseWidgetName];
+				}
+			}
+
+			for (var j = 0; j < renderPrefixCache.length; j++) {
+				for (var widgetClass in widgetPackage) {
+					if (widgetClass.toLowerCase() !=
+						(renderPrefixCache[j] + lowerCaseWidgetName)) { continue; }
+	
+					knownWidgetImplementations[lowerCaseWidgetName] =
+						widgetPackage[widgetClass];
+					return knownWidgetImplementations[lowerCaseWidgetName];
+				}
+			}
+		}
+		
+		throw new Error('Could not locate "' + widgetName + '" class');
+	}
+
+	// FIXME: does it even belong in this name space?
+	// NOTE: this method is implemented by DomWidget.js since not all
+	// hostenv's would have an implementation.
+	/*this.getWidgetFromPrimitive = function(baseRenderType){
+		dj_unimplemented("dojo.widget.manager.getWidgetFromPrimitive");
+	}
+
+	this.getWidgetFromEvent = function(nativeEvt){
+		dj_unimplemented("dojo.widget.manager.getWidgetFromEvent");
+	}*/
+
+	// Catch window resize events and notify top level widgets
+	this.resizing=false;
+	this.onResized = function() {
+		if(this.resizing){
+			return;	// duplicate event
+		}
+		try {
+			this.resizing=true;
+			for(var id in this.topWidgets) {
+				var child = this.topWidgets[id];
+				//dojo.debug("root resizing child " + child.widgetId);
+				if ( child.onResized ) {
+					child.onResized();
+				}
+			}
+		} finally {
+			this.resizing=false;
+		}
+	}
+	if(typeof window != "undefined") {
+		dojo.addOnLoad(this, 'onResized');							// initial sizing
+		dojo.event.connect(window, 'onresize', this, 'onResized');	// window resize
+	}
+
+	// FIXME: what else?
+}
+
+// copy the methods from the default manager (this) to the widget namespace
+dojo.widget.getUniqueId = function () { return dojo.widget.manager.getUniqueId.apply(dojo.widget.manager, arguments); }
+dojo.widget.addWidget = function () { return dojo.widget.manager.add.apply(dojo.widget.manager, arguments); }
+dojo.widget.destroyAllWidgets = function () { return dojo.widget.manager.destroyAll.apply(dojo.widget.manager, arguments); }
+dojo.widget.removeWidget = function () { return dojo.widget.manager.remove.apply(dojo.widget.manager, arguments); }
+dojo.widget.removeWidgetById = function () { return dojo.widget.manager.removeById.apply(dojo.widget.manager, arguments); }
+dojo.widget.getWidgetById = function () { return dojo.widget.manager.getWidgetById.apply(dojo.widget.manager, arguments); }
+dojo.widget.getWidgetsByType = function () { return dojo.widget.manager.getWidgetsByType.apply(dojo.widget.manager, arguments); }
+dojo.widget.getWidgetsByFilter = function () { return dojo.widget.manager.getWidgetsByFilter.apply(dojo.widget.manager, arguments); }
+dojo.widget.byId = function () { return dojo.widget.manager.getWidgetById.apply(dojo.widget.manager, arguments); }
+dojo.widget.byType = function () { return dojo.widget.manager.getWidgetsByType.apply(dojo.widget.manager, arguments); }
+dojo.widget.byFilter = function () { return dojo.widget.manager.getWidgetsByFilter.apply(dojo.widget.manager, arguments); }
+dojo.widget.byNode = function () { return dojo.widget.manager.getWidgetByNode.apply(dojo.widget.manager, arguments); }
+dojo.widget.all = function (n) {
+	var widgets = dojo.widget.manager.getAllWidgets.apply(dojo.widget.manager, arguments);
+	if(arguments.length > 0) {
+		return widgets[n];
+	}
+	return widgets;
+}
+dojo.widget.registerWidgetPackage = function () { return dojo.widget.manager.registerWidgetPackage.apply(dojo.widget.manager, arguments); }
+dojo.widget.getWidgetImplementation = function () { return dojo.widget.manager.getImplementation.apply(dojo.widget.manager, arguments); }
+dojo.widget.getWidgetImplementationName = function () { return dojo.widget.manager.getImplementationName.apply(dojo.widget.manager, arguments); }
+
+dojo.widget.widgets = dojo.widget.manager.widgets;
+dojo.widget.widgetIds = dojo.widget.manager.widgetIds;
+dojo.widget.root = dojo.widget.manager.root;

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,59 @@
+/*
+	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.Menu");
+dojo.provide("dojo.widget.DomMenu");
+
+dojo.deprecated("dojo.widget.Menu, dojo.widget.DomMenu",  "use dojo.widget.Menu2", "0.4");
+
+dojo.require("dojo.widget.*");
+
+dojo.widget.tags.addParseTreeHandler("dojo:menu");
+
+/* Menu
+ *******/
+
+dojo.widget.Menu = function () {
+	dojo.widget.Menu.superclass.constructor.call(this);
+}
+dojo.inherits(dojo.widget.Menu, dojo.widget.Widget);
+
+dojo.lang.extend(dojo.widget.Menu, {
+	widgetType: "Menu",
+	isContainer: true,
+	
+	items: [],
+	push: function(item){
+		dojo.connect.event(item, "onSelect", this, "onSelect");
+		this.items.push(item);
+	},
+	onSelect: function(){}
+});
+
+
+/* DomMenu
+ **********/
+
+dojo.widget.DomMenu = function(){
+	dojo.widget.DomMenu.superclass.constructor.call(this);
+}
+dojo.inherits(dojo.widget.DomMenu, dojo.widget.DomWidget);
+
+dojo.lang.extend(dojo.widget.DomMenu, {
+	widgetType: "Menu",
+	isContainer: true,
+
+	push: function (item) {
+		dojo.widget.Menu.call(this, item);
+		this.domNode.appendChild(item.domNode);
+	}
+});
+
+dojo.requireAfterIf("html", "dojo.widget.html.Menu");

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu2.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu2.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu2.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/Menu2.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,759 @@
+/*
+	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.Menu2");
+dojo.provide("dojo.widget.html.Menu2");
+dojo.provide("dojo.widget.PopupMenu2");
+dojo.provide("dojo.widget.MenuItem2");
+
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.event.*");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.HtmlWidget");
+
+
+dojo.widget.PopupMenu2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+	this.items = [];	// unused???
+	this.targetNodeIds = []; // fill this with nodeIds upon widget creation and it becomes context menu for those nodes
+	this.queueOnAnimationFinish = [];
+
+	this.eventNames =  {
+		open: ""
+	};
+
+}
+
+dojo.inherits(dojo.widget.PopupMenu2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.PopupMenu2, {
+	widgetType: "PopupMenu2",
+	isContainer: true,
+
+	snarfChildDomOutput: true,
+
+	currentSubmenu: null,
+	currentSubmenuTrigger: null,
+	parentMenu: null,
+	isShowing: false,
+	menuX: 0,
+	menuY: 0,
+	menuWidth: 0,
+	menuHeight: 0,
+	menuIndex: 0,
+
+	domNode: null,
+	containerNode: null,
+
+	eventNaming: "default",
+
+
+	templateString: '<div><div dojoAttachPoint="containerNode"></div></div>',
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlMenu2.css"),
+
+	itemHeight: 18,
+	iconGap: 1,
+	accelGap: 10,
+	submenuGap: 2,
+	finalGap: 5,
+	submenuIconSize: 4,
+	separatorHeight: 9,
+	submenuDelay: 500,
+	submenuOverlap: 5,
+	contextMenuForWindow: false,
+	openEvent: null,
+
+	submenuIconSrc: dojo.uri.dojoUri("src/widget/templates/images/submenu_off.gif").toString(),
+	submenuIconOnSrc: dojo.uri.dojoUri("src/widget/templates/images/submenu_on.gif").toString(),
+
+	initialize: function(args, frag) {
+
+		if (this.eventNaming == "default") {
+			for (eventName in this.eventNames) {
+				this.eventNames[eventName] = this.widgetId+"/"+eventName;
+			}
+		}
+
+	},
+
+	postCreate: function(){
+
+		dojo.html.addClass(this.domNode, 'dojoPopupMenu2');
+		dojo.html.addClass(this.containerNode, 'dojoPopupMenu2Client');
+
+		this.domNode.style.left = '-9999px'
+		this.domNode.style.top = '-9999px'
+
+		if (this.contextMenuForWindow){
+			var doc = document.documentElement  || document.body;
+			dojo.event.connect(doc, "oncontextmenu", this, "onOpen");
+		} else if ( this.targetNodeIds.length > 0 ){
+			for(var i=0; i<this.targetNodeIds.length; i++){
+				this.bindDomNode(this.targetNodeIds[i]);
+			}
+		}
+
+		this.subscribeSubitemsOnOpen();
+
+		this.layoutMenuSoon();
+	},
+
+	subscribeSubitemsOnOpen: function() {
+		var subItems = this.getChildrenOfType(dojo.widget.MenuItem2);
+
+		//dojo.debug(subItems)
+
+		for(var i=0; i<subItems.length; i++) {
+			//dojo.debug(subItems[i]);
+			dojo.event.topic.subscribe(this.eventNames.open, subItems[i], "menuOpen")
+		}
+	},
+
+	// get open event for current menu
+	getTopOpenEvent: function() {
+		var menu = this;
+		while (menu.parent){ menu = menu.parent; }
+		return menu.openEvent;
+	},
+
+	// attach menu to given node
+	bindDomNode: function(node){
+		dojo.event.connect(dojo.byId(node), "oncontextmenu", this, "onOpen");
+	},
+
+	// detach menu from given node
+	unBindDomNode: function(node){
+		dojo.event.kwDisconnect({
+			srcObj:     dojo.byId(node),
+			srcFunc:    "oncontextmenu",
+			targetObj:  this,
+			targetFunc: "onOpen",
+			once:       true
+		});
+	},
+
+	layoutMenuSoon: function(){
+
+		dojo.lang.setTimeout(this, "layoutMenu", 0);
+	},
+
+	layoutMenu: function(){
+
+        // menu must be attached to DOM for size calculations to work
+
+        var parent = this.domNode.parentNode;
+        if (! parent || parent == undefined) {
+            document.body.appendChild(this.domNode);
+        }
+
+        // determine menu width
+
+		var max_label_w = 0;
+		var max_accel_w = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			if (this.children[i].getLabelWidth){
+
+				max_label_w = Math.max(max_label_w, this.children[i].getLabelWidth());
+			}
+
+			if (dojo.lang.isFunction(this.children[i].getAccelWidth)){
+
+				max_accel_w = Math.max(max_accel_w, this.children[i].getAccelWidth());
+			}
+		}
+
+		if( isNaN(max_label_w) || isNaN(max_accel_w) ){
+			// Browser needs some more time to calculate sizes
+			this.layoutMenuSoon();
+			return;
+		}
+
+		var clientLeft = dojo.style.getPixelValue(this.domNode, "padding-left", true) + dojo.style.getPixelValue(this.containerNode, "padding-left", true);
+		var clientTop  = dojo.style.getPixelValue(this.domNode, "padding-top", true)  + dojo.style.getPixelValue(this.containerNode, "padding-top", true);
+
+		if( isNaN(clientLeft) || isNaN(clientTop) ){
+			// Browser needs some more time to calculate sizes
+			this.layoutMenuSoon();
+			return;
+		}
+
+		var y = clientTop;
+		var max_item_width = 0;
+
+		for(var i=0; i<this.children.length; i++){
+
+			var ch = this.children[i];
+
+			ch.layoutItem(max_label_w, max_accel_w);
+
+			ch.topPosition = y;
+
+			y += dojo.style.getOuterHeight(ch.domNode);
+			max_item_width = Math.max(max_item_width, dojo.style.getOuterWidth(ch.domNode));
+		}
+
+		dojo.style.setContentWidth(this.containerNode, max_item_width);
+		dojo.style.setContentHeight(this.containerNode, y-clientTop);
+
+		dojo.style.setContentWidth(this.domNode, dojo.style.getOuterWidth(this.containerNode));
+		dojo.style.setContentHeight(this.domNode, dojo.style.getOuterHeight(this.containerNode));
+
+		this.menuWidth = dojo.style.getOuterWidth(this.domNode);
+		this.menuHeight = dojo.style.getOuterHeight(this.domNode);
+	},
+
+	open: function(x, y, parentMenu, explodeSrc){
+
+		// NOTE: alex:
+		//	this couldn't have possibly worked. this.open wound up calling
+		//	this.close, which called open...etc..
+		if (this.isShowing){ /* this.close(); */ return; }
+
+		if ( !parentMenu ) {
+			// record whenever a top level menu is opened
+			dojo.widget.html.Menu2Manager.opened(this, explodeSrc);
+		}
+
+		//dojo.debug("open called for animation "+this.animationInProgress)
+
+		// if I click  right button and menu is opened, then it gets 2 commands: close -> open
+		// so close enables animation and next "open" is put to queue to occur at new location
+		if(this.animationInProgress){
+			this.queueOnAnimationFinish.push(this.open, arguments);
+			return;
+		}
+
+		var viewport = dojo.html.getViewportSize();
+		var scrolloffset = dojo.html.getScrollOffset();
+
+		var clientRect = {
+			'left'  : scrolloffset[0],
+			'right' : scrolloffset[0] + viewport[0],
+			'top'   : scrolloffset[1],
+			'bottom': scrolloffset[1] + viewport[1]
+		};
+
+		if (parentMenu){
+			// submenu is opening
+
+			if (x + this.menuWidth > clientRect.right){ x = x - (this.menuWidth + parentMenu.menuWidth - (2 * this.submenuOverlap)); }
+
+			if (y + this.menuHeight > clientRect.bottom){ y = y -
+			(this.menuHeight - (this.itemHeight + 5)); } // TODO: why 5?
+
+		}else{
+			// top level menu is opening
+			x+=scrolloffset[0];
+			y+=scrolloffset[1];
+			explodeSrc[0] += scrolloffset[0];
+			explodeSrc[1] += scrolloffset[1];
+
+			if (x < clientRect.left){ x = clientRect.left; }
+			if (x + this.menuWidth > clientRect.right){ x = x - this.menuWidth; }
+
+			if (y < clientRect.top){ y = clientRect.top; }
+			if (y + this.menuHeight > clientRect.bottom){ y = y - this.menuHeight; }
+		}
+
+		this.parentMenu = parentMenu;
+		this.explodeSrc = explodeSrc;
+		this.menuIndex = parentMenu ? parentMenu.menuIndex + 1 : 1;
+
+		this.menuX = x;
+		this.menuY = y;
+
+		// move the menu into position but make it invisible
+		// (because when menus are initially constructed they are visible but off-screen)
+		this.domNode.style.zIndex = 200 + this.menuIndex;
+		this.domNode.style.left = x + 'px';
+		this.domNode.style.top = y + 'px';
+		this.domNode.style.display='none';
+		this.domNode.style.position='absolute';
+
+		// then use the user defined method to display it
+		this.show();
+
+		this.isShowing = true;
+	},
+
+	close: function(){
+		// If we are in the process of opening the menu and we are asked to close it,
+		// we should really cancel the current animation, but for simplicity we will
+		// just ignore the request
+		if(this.animationInProgress){
+			this.queueOnAnimationFinish.push(this.close, []);
+			return;
+		}
+
+		this.closeSubmenu();
+		this.hide();
+		this.isShowing = false;
+		dojo.widget.html.Menu2Manager.closed(this);
+	},
+
+	onShow: function() {
+		dojo.widget.HtmlWidget.prototype.onShow.call(this);
+		this.processQueue();
+	},
+
+	// do events from queue
+	processQueue: function() {
+		if (!this.queueOnAnimationFinish.length) return;
+
+		var func = this.queueOnAnimationFinish.shift();
+		var args = this.queueOnAnimationFinish.shift();
+
+		func.apply(this, args);
+	},
+
+	onHide: function() {
+		dojo.widget.HtmlWidget.prototype.onHide.call(this);
+
+		this.processQueue();
+	},
+
+
+	closeAll: function(){
+
+		if (this.parentMenu){
+			this.parentMenu.closeAll();
+		}else{
+			this.close();
+		}
+	},
+
+	closeSubmenu: function(){
+		if (this.currentSubmenu == null){ return; }
+
+		this.currentSubmenu.close();
+		this.currentSubmenu = null;
+
+		this.currentSubmenuTrigger.is_open = false;
+		this.currentSubmenuTrigger.closedSubmenu();
+		this.currentSubmenuTrigger = null;
+	},
+
+	openSubmenu: function(submenu, from_item){
+
+		var our_x = dojo.style.getPixelValue(this.domNode, 'left');
+		var our_y = dojo.style.getPixelValue(this.domNode, 'top');
+		var our_w = dojo.style.getOuterWidth(this.domNode);
+		var item_y = from_item.topPosition;
+
+		var x = our_x + our_w - this.submenuOverlap;
+		var y = our_y + item_y;
+
+		this.currentSubmenu = submenu;
+		this.currentSubmenu.open(x, y, this, from_item.domNode);
+
+		this.currentSubmenuTrigger = from_item;
+		this.currentSubmenuTrigger.is_open = true;
+	},
+
+	onOpen: function(e){
+		this.openEvent = e;
+
+		//dojo.debugShallow(e);
+		this.open(e.clientX, e.clientY, null, [e.clientX, e.clientY]);
+
+		if(e["preventDefault"]){
+			e.preventDefault();
+		}
+	},
+
+	isPointInMenu: function(x, y){
+
+		if (x < this.menuX){ return 0; }
+		if (x > this.menuX + this.menuWidth){ return 0; }
+
+		if (y < this.menuY){ return 0; }
+		if (y > this.menuY + this.menuHeight){ return 0; }
+
+		return 1;
+	}
+});
+
+
+dojo.widget.MenuItem2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+
+	this.eventNames = {
+		engage: ""
+	};
+}
+
+dojo.inherits(dojo.widget.MenuItem2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.MenuItem2, {
+	widgetType: "MenuItem2",
+	templateString:
+			 '<div class="dojoMenuItem2">'
+			+'<div dojoAttachPoint="iconNode" class="dojoMenuItem2Icon"></div>'
+			+'<span dojoAttachPoint="labelNode" class="dojoMenuItem2Label"><span><span></span></span></span>'
+			+'<span dojoAttachPoint="accelNode" class="dojoMenuItem2Accel"><span><span></span></span></span>'
+			+'<div dojoAttachPoint="submenuNode" class="dojoMenuItem2Submenu"></div>'
+			+'<div dojoAttachPoint="targetNode" class="dojoMenuItem2Target" dojoAttachEvent="onMouseOver: onHover; onMouseOut: onUnhover; onClick: _onClick;">&nbsp;</div>'
+			+'</div>',
+
+	//
+	// nodes
+	//
+
+	domNode: null,
+	iconNode: null,
+	labelNode: null,
+	accelNode: null,
+	submenuNode: null,
+	targetNode: null,
+
+	//
+	// internal settings
+	//
+
+	is_hovering: false,
+	hover_timer: null,
+	is_open: false,
+	topPosition: 0,
+
+	//
+	// options
+	//
+
+	caption: 'Untitled',
+	accelKey: '',
+	iconSrc: '',
+	submenuId: '',
+	disabled: false,
+	eventNaming: "default",
+
+
+	postCreate: function(){
+
+		dojo.html.disableSelection(this.domNode);
+
+		if (this.disabled){
+			this.setDisabled(true);
+		}
+
+		this.labelNode.childNodes[0].appendChild(document.createTextNode(this.caption));
+		this.accelNode.childNodes[0].appendChild(document.createTextNode(this.accelKey));
+
+		this.labelShadowNode = this.labelNode.childNodes[0].childNodes[0];
+		this.accelShadowNode = this.accelNode.childNodes[0].childNodes[0];
+
+		this.labelShadowNode.appendChild(document.createTextNode(this.caption));
+		this.accelShadowNode.appendChild(document.createTextNode(this.accelKey));
+
+		if (this.eventNaming == "default") {
+			for (eventName in this.eventNames) {
+				this.eventNames[eventName] = this.widgetId+"/"+eventName;
+			}
+		}
+	},
+
+	layoutItem: function(label_w, accel_w){
+
+		var x_label = this.parent.itemHeight + this.parent.iconGap;
+		var x_accel = x_label + label_w + this.parent.accelGap;
+		var x_submu = x_accel + accel_w + this.parent.submenuGap;
+		var total_w = x_submu + this.parent.submenuIconSize + this.parent.finalGap;
+
+
+		this.iconNode.style.left = '0px';
+		this.iconNode.style.top = '0px';
+
+
+		if (this.iconSrc){
+
+			if ((this.iconSrc.toLowerCase().substring(this.iconSrc.length-4) == ".png") && (dojo.render.html.ie)){
+
+				this.iconNode.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+this.iconSrc+"', sizingMethod='image')";
+				this.iconNode.style.backgroundImage = '';
+			}else{
+				this.iconNode.style.backgroundImage = 'url('+this.iconSrc+')';
+			}
+		}else{
+			this.iconNode.style.backgroundImage = '';
+		}
+
+		dojo.style.setOuterWidth(this.iconNode, this.parent.itemHeight);
+		dojo.style.setOuterHeight(this.iconNode, this.parent.itemHeight);
+
+		dojo.style.setOuterHeight(this.labelNode, this.parent.itemHeight);
+		dojo.style.setOuterHeight(this.accelNode, this.parent.itemHeight);
+
+		dojo.style.setContentWidth(this.domNode, total_w);
+		dojo.style.setContentHeight(this.domNode, this.parent.itemHeight);
+
+		this.labelNode.style.left = x_label + 'px';
+		this.accelNode.style.left = x_accel + 'px';
+		this.submenuNode.style.left = x_submu + 'px';
+
+		dojo.style.setOuterWidth(this.submenuNode, this.parent.submenuIconSize);
+		dojo.style.setOuterHeight(this.submenuNode, this.parent.itemHeight);
+
+		this.submenuNode.style.display = this.submenuId ? 'block' : 'none';
+		this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconSrc+')';
+
+		dojo.style.setOuterWidth(this.targetNode, total_w);
+		dojo.style.setOuterHeight(this.targetNode, this.parent.itemHeight);
+	},
+
+	onHover: function(){
+
+		if (this.is_hovering){ return; }
+		if (this.is_open){ return; }
+
+		this.parent.closeSubmenu();
+		this.highlightItem();
+
+		if (this.is_hovering){ this.stopSubmenuTimer(); }
+		this.is_hovering = 1;
+		this.startSubmenuTimer();
+	},
+
+	onUnhover: function(){
+
+		if (!this.is_open){ this.unhighlightItem(); }
+
+		this.is_hovering = 0;
+		this.stopSubmenuTimer();
+	},
+
+	// Internal function for clicks
+	_onClick: function(){
+
+		if (this.disabled){ return; }
+
+		if (this.submenuId){
+
+			if (!this.is_open){
+				this.stopSubmenuTimer();
+				this.openSubmenu();
+			}
+
+		}else{
+			this.parent.closeAll();
+		}
+
+		this.onClick();
+		//dojo.debug("GO "+this.eventNames.engage)
+		dojo.event.topic.publish(this.eventNames.engage, this);
+	},
+
+	// User defined function to handle clicks
+	onClick: function() { },
+
+	highlightItem: function(){
+
+		dojo.html.addClass(this.domNode, 'dojoMenuItem2Hover');
+		this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconOnSrc+')';
+	},
+
+	unhighlightItem: function(){
+
+		dojo.html.removeClass(this.domNode, 'dojoMenuItem2Hover');
+		this.submenuNode.style.backgroundImage = 'url('+this.parent.submenuIconSrc+')';
+	},
+
+	startSubmenuTimer: function(){
+		this.stopSubmenuTimer();
+
+		if (this.disabled){ return; }
+
+		var self = this;
+		var closure = function(){ return function(){ self.openSubmenu(); } }();
+
+		this.hover_timer = window.setTimeout(closure, this.parent.submenuDelay);
+	},
+
+	stopSubmenuTimer: function(){
+		if (this.hover_timer){
+			window.clearTimeout(this.hover_timer);
+			this.hover_timer = null;
+		}
+	},
+
+	openSubmenu: function(){
+		// first close any other open submenu
+		this.parent.closeSubmenu();
+
+		var submenu = dojo.widget.getWidgetById(this.submenuId);
+		if (submenu){
+
+			this.parent.openSubmenu(submenu, this);
+		}
+
+		//dojo.debug('open submenu for item '+this.widgetId);
+	},
+
+	closedSubmenu: function(){
+
+		this.onUnhover();
+	},
+
+	setDisabled: function(value){
+		this.disabled = value;
+
+		if (this.disabled){
+			dojo.html.addClass(this.domNode, 'dojoMenuItem2Disabled');
+		}else{
+			dojo.html.removeClass(this.domNode, 'dojoMenuItem2Disabled');
+		}
+	},
+
+	getLabelWidth: function(){
+
+		var node = this.labelNode.childNodes[0];
+
+		return dojo.style.getOuterWidth(node);
+	},
+
+	getAccelWidth: function(){
+
+		var node = this.accelNode.childNodes[0];
+
+		return dojo.style.getOuterWidth(node);
+	},
+
+	menuOpen: function(message) {
+	}
+
+});
+
+
+dojo.widget.MenuSeparator2 = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.MenuSeparator2, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.MenuSeparator2, {
+	widgetType: "MenuSeparator2",
+
+	domNode: null,
+	topNode: null,
+	bottomNode: null,
+
+	templateString: '<div>'
+			+'<div dojoAttachPoint="topNode"></div>'
+			+'<div dojoAttachPoint="bottomNode"></div>'
+			+'</div>',
+
+	postCreate: function(){
+
+		dojo.html.addClass(this.domNode, 'dojoMenuSeparator2');
+		dojo.html.addClass(this.topNode, 'dojoMenuSeparator2Top');
+		dojo.html.addClass(this.bottomNode, 'dojoMenuSeparator2Bottom');
+
+		dojo.html.disableSelection(this.domNode);
+
+		this.layoutItem();
+	},
+
+	layoutItem: function(label_w, accel_w){
+
+		var full_width = this.parent.itemHeight
+				+ this.parent.iconGap
+				+ label_w
+				+ this.parent.accelGap
+				+ accel_w
+				+ this.parent.submenuGap
+				+ this.parent.submenuIconSize
+				+ this.parent.finalGap;
+
+		if (isNaN(full_width)){ return; }
+
+		dojo.style.setContentHeight(this.domNode, this.parent.separatorHeight);
+		dojo.style.setContentWidth(this.domNode, full_width);
+	}
+});
+
+//
+// the menu manager makes sure we don't have several menus
+// open at once. the root menu in an opening sequence calls
+// opened(). when a root menu closes it calls closed(). then
+// everything works. lovely.
+//
+
+dojo.widget.html.Menu2Manager = new function(){
+
+	this.currentMenu = null;
+	this.currentButton = null;		// button that opened current menu (if any)
+	this.focusNode = null;
+
+	dojo.event.connect(document, 'onmousedown', this, 'onClick');
+	dojo.event.connect(window, "onscroll", this, "onClick");
+
+	this.closed = function(menu){
+		if (this.currentMenu == menu){
+			this.currentMenu = null;
+			this.currentButton = null;
+		}
+	};
+
+	this.opened = function(menu, button){
+		if (menu == this.currentMenu){ return; }
+
+		if (this.currentMenu){
+			this.currentMenu.close();
+		}
+
+		this.currentMenu = menu;
+		this.currentButton = button;
+	};
+
+	this.onClick = function(e){
+
+		if (!this.currentMenu){ return; }
+
+		var scrolloffset = dojo.html.getScrollOffset();
+
+		var x = e.clientX + scrolloffset[0];
+		var y = e.clientY + scrolloffset[1];
+
+		var m = this.currentMenu;
+
+		// starting from the base menu, perform a hit test
+		// and exit when one succeeds
+
+		while (m){
+
+			if (m.isPointInMenu(x, y)){
+
+				return;
+			}
+
+			m = m.currentSubmenu;
+		}
+
+		// Also, if user clicked the button that opened this menu, then
+		// that button will send the menu a close() command, so this code
+		// shouldn't try to close the menu.  Closing twice messes up animation.
+		if (this.currentButton && dojo.html.overElement(this.currentButton, e)){
+			return;
+		}
+
+		// the click didn't fall within the open menu tree
+		// so close it
+
+		this.currentMenu.close();
+	};
+}
+
+
+// make it a tag
+dojo.widget.tags.addParseTreeHandler("dojo:PopupMenu2");
+dojo.widget.tags.addParseTreeHandler("dojo:MenuItem2");
+dojo.widget.tags.addParseTreeHandler("dojo:MenuSeparator2");
+

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

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/MonthlyCalendar.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/MonthlyCalendar.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/MonthlyCalendar.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/MonthlyCalendar.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.MonthlyCalendar");
+dojo.provide("dojo.widget.MonthlyCalendar.util");
+dojo.require("dojo.widget.DomWidget");
+dojo.require("dojo.date");
+
+dojo.widget.MonthlyCalendar= function(){
+	dojo.widget.Widget.call(this);
+	this.widgetType = "MonthlyCalendar";
+	this.isContainer = false;
+	// the following aliases prevent breaking people using 0.2.x
+	this.months = dojo.date.months;
+	this.weekdays = dojo.date.days;
+	this.toRfcDate = dojo.widget.MonthlyCalendar.util.toRfcDate;
+	this.fromRfcDate = dojo.widget.MonthlyCalendar.util.fromRfcDate;
+	this.initFirstSaturday = dojo.widget.MonthlyCalendar.util.initFirstSaturday;
+}
+
+dojo.inherits(dojo.widget.MonthlyCalendar, dojo.widget.Widget);
+dojo.widget.tags.addParseTreeHandler("dojo:monthlycalendar");
+
+dojo.requireAfterIf("html", "dojo.widget.html.MonthlyCalendar");
+
+dojo.widget.MonthlyCalendar.util= new function() {
+	this.months = dojo.date.months;
+	this.weekdays = dojo.date.days;
+	
+	this.toRfcDate = function(jsDate) {
+		if(!jsDate) {
+			jsDate = this.today;
+		}
+		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();
+		}
+		// because this is a date picker and not a time picker, we treat time 
+		// as zero
+		return year + "-" + month + "-" + date + "T00:00:00+00:00";
+	}
+	
+	this.fromRfcDate = function(rfcDate) {
+		var tempDate = rfcDate.split("-");
+		if(tempDate.length < 3) {
+			return new Date();
+		}
+		// fullYear, month, date
+		return new Date(parseInt(tempDate[0]), (parseInt(tempDate[1], 10) - 1), parseInt(tempDate[2].substr(0,2), 10));
+	}
+	
+	this.initFirstSaturday = function(month, year) {
+		if(!month) {
+			month = this.date.getMonth();
+		}
+		if(!year) {
+			year = this.date.getFullYear();
+		}
+		var firstOfMonth = new Date(year, month, 1);
+		return {year: year, month: month, date: 7 - firstOfMonth.getDay()};
+	}
+}

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

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

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizableTextarea.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,110 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.ResizableTextarea");
+dojo.require("dojo.html");
+dojo.require("dojo.widget.*");
+dojo.require("dojo.widget.LayoutPane");
+dojo.require("dojo.widget.ResizeHandle");
+
+dojo.widget.tags.addParseTreeHandler("dojo:resizabletextarea");
+
+dojo.widget.ResizableTextarea = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.ResizableTextarea, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.ResizableTextarea, {
+	templatePath: dojo.uri.dojoUri("src/widget/templates/HtmlResizableTextarea.html"),
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlResizableTextarea.css"),
+	widgetType: "ResizableTextarea",
+	tagName: "dojo:resizabletextarea",
+	isContainer: false,
+	textAreaNode: null,
+	textAreaContainer: null,
+	textAreaContainerNode: null,
+	statusBar: null,
+	statusBarContainerNode: null,
+	statusLabelNode: null,
+	statusLabel: null,
+	rootLayoutNode: null,
+	resizeHandleNode: null,
+	resizeHandle: null,
+
+	fillInTemplate: function(args, frag){
+		this.textAreaNode = this.getFragNodeRef(frag).cloneNode(true);
+
+		// FIXME: Safari apparently needs this!
+		document.body.appendChild(this.domNode);
+
+		this.rootLayout = dojo.widget.createWidget(
+			"LayoutPane",
+			{
+				minHeight: 50,
+				minWidth: 100
+			},
+			this.rootLayoutNode
+		);
+
+
+		this.textAreaContainer = dojo.widget.createWidget(
+			"LayoutPane",
+			{ layoutAlign: "client" },
+			this.textAreaContainerNode
+		);
+		this.rootLayout.addChild(this.textAreaContainer);
+
+		this.textAreaContainer.domNode.appendChild(this.textAreaNode);
+		with(this.textAreaNode.style){
+			width="100%";
+			height="100%";
+		}
+
+		this.statusBar = dojo.widget.createWidget(
+			"LayoutPane",
+			{ 
+				layoutAlign: "bottom", 
+				minHeight: 28
+			},
+			this.statusBarContainerNode
+		);
+		this.rootLayout.addChild(this.statusBar);
+
+		this.statusLabel = dojo.widget.createWidget(
+			"LayoutPane",
+			{ 
+				layoutAlign: "client", 
+				minWidth: 50
+			},
+			this.statusLabelNode
+		);
+		this.statusBar.addChild(this.statusLabel);
+
+		this.resizeHandle = dojo.widget.createWidget(
+			"ResizeHandle", 
+			{ targetElmId: this.rootLayout.widgetId },
+			this.resizeHandleNode
+		);
+		this.statusBar.addChild(this.resizeHandle);
+		// dojo.debug(this.rootLayout.widgetId);
+
+		// dojo.event.connect(this.resizeHandle, "beginSizing", this, "hideContent");
+		// dojo.event.connect(this.resizeHandle, "endSizing", this, "showContent");
+	},
+
+	hideContent: function(){
+		this.textAreaNode.style.display = "none";
+	},
+
+	showContent: function(){
+		this.textAreaNode.style.display = "";
+	}
+});

Added: jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js
URL: http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js?rev=378118&view=auto
==============================================================================
--- jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js (added)
+++ jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/widget/ResizeHandle.js Wed Feb 15 15:30:01 2006
@@ -0,0 +1,101 @@
+/*
+	Copyright (c) 2004-2005, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.widget.ResizeHandle");
+dojo.provide("dojo.widget.html.ResizeHandle");
+
+dojo.require("dojo.widget.*");
+dojo.require("dojo.html");
+dojo.require("dojo.style");
+dojo.require("dojo.dom");
+dojo.require("dojo.event");
+
+dojo.widget.html.ResizeHandle = function(){
+	dojo.widget.HtmlWidget.call(this);
+}
+
+dojo.inherits(dojo.widget.html.ResizeHandle, dojo.widget.HtmlWidget);
+
+dojo.lang.extend(dojo.widget.html.ResizeHandle, {
+	widgetType: "ResizeHandle",
+
+	isSizing: false,
+	startPoint: null,
+	startSize: null,
+
+	grabImg: null,
+
+	targetElmId: '',
+	imgSrc: dojo.uri.dojoUri("src/widget/templates/grabCorner.gif"),
+
+	templateCssPath: dojo.uri.dojoUri("src/widget/templates/HtmlResizeHandle.css"),
+	templateString: '<div dojoAttachPoint="domNode"><img dojoAttachPoint="grabImg" /></div>',
+
+	fillInTemplate: function(){
+
+		dojo.style.insertCssFile(this.templateCssPath);
+
+		dojo.html.addClass(this.domNode, 'dojoHtmlResizeHandle');
+		dojo.html.addClass(this.grabImg, 'dojoHtmlResizeHandleImage');
+
+		this.grabImg.src = this.imgSrc;
+	},
+
+	postCreate: function(){
+		dojo.event.connect(this.domNode, "onmousedown", this, "beginSizing");
+	},
+
+	beginSizing: function(e){
+		if (this.isSizing){ return false; }
+
+		this.targetElm = dojo.widget.byId(this.targetElmId);
+		if (!this.targetElm){ return; }
+
+		var screenX = window.event ? window.event.clientX : e.pageX;
+		var screenY = window.event ? window.event.clientY : e.pageY;
+
+		this.isSizing = true;
+		this.startPoint  = {'x':e.clientX, 'y':e.clientY};
+		this.startSize  = {'w':dojo.style.getOuterWidth(this.targetElm.domNode), 'h':dojo.style.getOuterHeight(this.targetElm.domNode)};
+
+		dojo.event.kwConnect({
+			srcObj: document.body, 
+			srcFunc: "onmousemove",
+			targetObj: this,
+			targetFunc: "changeSizing",
+			rate: 25
+		});
+		dojo.event.connect(document.body, "onmouseup", this, "endSizing");
+
+		e.preventDefault();
+	},
+
+	changeSizing: function(e){
+		// On IE, if you move the mouse above/to the left of the object being resized,
+		// sometimes clientX/Y aren't set, apparently.  Just ignore the event.
+		if(!e.clientX  || !e.clientY){ return; }
+		var dx = this.startPoint.x - e.clientX;
+		var dy = this.startPoint.y - e.clientY;
+		this.targetElm.resizeTo(this.startSize.w - dx, this.startSize.h - dy);
+
+		e.preventDefault();
+	},
+
+	endSizing: function(e){
+		dojo.event.disconnect(document.body, "onmousemove", this, "changeSizing");
+		dojo.event.disconnect(document.body, "onmouseup", this, "endSizing");
+
+		this.isSizing = false;
+	}
+
+
+});
+
+dojo.widget.tags.addParseTreeHandler("dojo:ResizeHandle");



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