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/09/29 05:43:06 UTC

svn commit: r451106 [16/40] - in /tapestry/tapestry4/trunk: ./ tapestry-framework/src/java/org/apache/tapestry/asset/ tapestry-framework/src/js/dojo/ tapestry-framework/src/js/dojo/src/ tapestry-framework/src/js/dojo/src/animation/ tapestry-framework/s...

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/iCalendar.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/iCalendar.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/iCalendar.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/iCalendar.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,815 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.iCalendar");
+dojo.require("dojo.lang.common");
+dojo.require("dojo.text.textDirectory");
+dojo.require("dojo.date.common");
+dojo.require("dojo.date.serialize");
+
+
+dojo.iCalendar.fromText =  function (/* string */text) {
+	// summary
+	// Parse text of an iCalendar and return an array of iCalendar objects
+
+	var properties = dojo.textDirectoryTokeniser.tokenise(text);
+	var calendars = [];
+
+	//dojo.debug("Parsing iCal String");
+	for (var i = 0, begun = false; i < properties.length; i++) {
+		var prop = properties[i];
+		if (!begun) {
+			if (prop.name == 'BEGIN' && prop.value == 'VCALENDAR') {
+				begun = true;
+				var calbody = [];
+			}
+		} else if (prop.name == 'END' && prop.value == 'VCALENDAR') {
+			calendars.push(new dojo.iCalendar.VCalendar(calbody));
+			begun = false;
+		} else {
+			calbody.push(prop);
+		}
+	}
+	return /* array */calendars;
+}
+
+
+dojo.iCalendar.Component = function (/* string */ body ) {
+	// summary
+	// A component is the basic container of all this stuff. 
+
+	if (!this.name) {
+		this.name = "COMPONENT"
+	}
+
+	this.properties = [];
+	this.components = [];
+
+	if (body) {
+		for (var i = 0, context = ''; i < body.length; i++) {
+			if (context == '') {
+				if (body[i].name == 'BEGIN') {
+					context = body[i].value;
+					var childprops = [];
+				} else {
+					this.addProperty(new dojo.iCalendar.Property(body[i]));
+				}
+			} else if (body[i].name == 'END' && body[i].value == context) {
+				if (context=="VEVENT") {
+					this.addComponent(new dojo.iCalendar.VEvent(childprops));
+				} else if (context=="VTIMEZONE") {
+					this.addComponent(new dojo.iCalendar.VTimeZone(childprops));
+				} else if (context=="VTODO") {
+					this.addComponent(new dojo.iCalendar.VTodo(childprops));
+				} else if (context=="VJOURNAL") {
+					this.addComponent(new dojo.iCalendar.VJournal(childprops));
+				} else if (context=="VFREEBUSY") {
+					this.addComponent(new dojo.iCalendar.VFreeBusy(childprops));
+				} else if (context=="STANDARD") {
+					this.addComponent(new dojo.iCalendar.Standard(childprops));
+				} else if (context=="DAYLIGHT") {
+					this.addComponent(new dojo.iCalendar.Daylight(childprops));
+				} else if (context=="VALARM") {
+					this.addComponent(new dojo.iCalendar.VAlarm(childprops));
+				}else {
+					dojo.unimplemented("dojo.iCalendar." + context);
+				}
+				context = '';
+			} else {
+				childprops.push(body[i]);
+			}
+		}
+
+		if (this._ValidProperties) {
+			this.postCreate();
+		}
+	}
+}
+
+dojo.extend(dojo.iCalendar.Component, {
+
+	addProperty: function (prop) {
+		// summary
+		// push a new property onto a component.
+		this.properties.push(prop);
+		this[prop.name.toLowerCase()] = prop;
+	},
+
+	addComponent: function (prop) {
+		// summary
+		// add a component to this components list of children.
+		this.components.push(prop);
+	},
+
+	postCreate: function() {
+		for (var x=0; x<this._ValidProperties.length; x++) {
+			var evtProperty = this._ValidProperties[x];
+			var found = false;
+	
+			for (var y=0; y<this.properties.length; y++) {	
+				var prop = this.properties[y];
+				var propName = prop.name.toLowerCase();
+				if (dojo.lang.isArray(evtProperty)) {
+
+					var alreadySet = false;
+					for (var z=0; z<evtProperty.length; z++) {
+						var evtPropertyName = evtProperty[z].name.toLowerCase();
+						if((this[evtPropertyName])  && (evtPropertyName != propName )) {
+							alreadySet=true;
+						} 
+					}
+					if (!alreadySet) {
+						this[propName] = prop;
+					}
+				} else {
+					if (propName == evtProperty.name.toLowerCase()) {
+						found = true;
+						if (evtProperty.occurance == 1){
+							this[propName] = prop;
+						} else {
+							found = true;
+							if (!dojo.lang.isArray(this[propName])) {
+							 	this[propName] = [];
+							}
+							this[propName].push(prop);
+						}
+					}
+				}
+			}
+
+			if (evtProperty.required && !found) {	
+				dojo.debug("iCalendar - " + this.name + ": Required Property not found: " + evtProperty.name);
+			}
+		}
+
+		// parse any rrules		
+		if (dojo.lang.isArray(this.rrule)) {
+			for(var x=0; x<this.rrule.length; x++) {
+				var rule = this.rrule[x].value;
+
+				//add a place to cache dates we have checked for recurrance
+				this.rrule[x].cache = function() {};
+				
+				var temp = rule.split(";");
+				for (var y=0; y<temp.length; y++) {
+					var pair = temp[y].split("=");
+					var key = pair[0].toLowerCase();
+					var val = pair[1];
+
+					if ((key == "freq") || (key=="interval") || (key=="until")) {
+						this.rrule[x][key]= val;
+					} else {
+						var valArray = val.split(",");
+						this.rrule[x][key] = valArray; 
+					}
+				}	
+			}
+			this.recurring = true;
+		}
+
+	}, 
+
+	toString: function () {
+		// summary
+		// output a string representation of this component.
+		return "[iCalendar.Component; " + this.name + ", " + this.properties.length +
+			" properties, " + this.components.length + " components]";
+	}
+});
+
+dojo.iCalendar.Property = function (prop) {
+	// summary
+	// A single property of a component.
+
+	// unpack the values
+	this.name = prop.name;
+	this.group = prop.group;
+	this.params = prop.params;
+	this.value = prop.value;
+
+}
+
+dojo.extend(dojo.iCalendar.Property, {
+	toString: function () {	
+		// summary
+		// output a string reprensentation of this component.
+		return "[iCalenday.Property; " + this.name + ": " + this.value + "]";
+	}
+});
+
+// This is just a little helper function for the Component Properties
+var _P = function (n, oc, req) {
+	return {name: n, required: (req) ? true : false,
+		occurance: (oc == '*' || !oc) ? -1 : oc}
+}
+
+/*
+ * VCALENDAR
+ */
+
+dojo.iCalendar.VCalendar = function (/* string */ calbody) {
+	// summary
+	// VCALENDAR Component
+
+	this.name = "VCALENDAR";
+	this.recurring = [];
+	this.nonRecurringEvents = function(){};
+	dojo.iCalendar.Component.call(this, calbody);
+}
+
+dojo.inherits(dojo.iCalendar.VCalendar, dojo.iCalendar.Component);
+
+dojo.extend(dojo.iCalendar.VCalendar, {
+
+	addComponent: function (prop) {
+		// summary
+		// add component to the calenadar that makes it easy to pull them out again later.
+		this.components.push(prop);
+		if (prop.name.toLowerCase() == "vevent") {
+			if (prop.rrule) {
+				this.recurring.push(prop);
+			} else {
+				var startDate = prop.getDate();
+				var month = startDate.getMonth() + 1;
+				var dateString= month + "-" + startDate.getDate() + "-" + startDate.getFullYear();
+				if (!dojo.lang.isArray(this[dateString])) {
+					this.nonRecurringEvents[dateString] = [];
+				}
+				this.nonRecurringEvents[dateString].push(prop);
+			}
+		}
+	},
+
+	preComputeRecurringEvents: function(until) {
+		var calculatedEvents = function(){};
+
+		for(var x=0; x<this.recurring.length; x++) {
+			var dates = this.recurring[x].getDates(until);
+			for (var y=0; y<dates.length;y++) {
+				var month = dates[y].getMonth() + 1;
+				var dateStr = month + "-" + dates[y].getDate() + "-" + dates[y].getFullYear();
+				if (!dojo.lang.isArray(calculatedEvents[dateStr])) {
+					calculatedEvents[dateStr] = [];
+				}
+
+				if (!dojo.lang.inArray(calculatedEvents[dateStr], this.recurring[x])) { 
+					calculatedEvents[dateStr].push(this.recurring[x]);
+				} 
+			}
+		}
+		this.recurringEvents = calculatedEvents;
+	
+	},
+
+	getEvents: function(/* Date */ date) {
+		// summary
+		// Gets all events occuring on a particular date
+		var events = [];
+		var recur = [];
+		var nonRecur = [];
+		var month = date.getMonth() + 1;
+		var dateStr= month + "-" + date.getDate() + "-" + date.getFullYear();
+		if (dojo.lang.isArray(this.nonRecurringEvents[dateStr])) {
+			nonRecur= this.nonRecurringEvents[dateStr];
+			dojo.debug("Number of nonRecurring Events: " + nonRecur.length);
+		} 
+		
+
+		if (dojo.lang.isArray(this.recurringEvents[dateStr])) {
+			recur= this.recurringEvents[dateStr];
+		} 
+
+		events = recur.concat(nonRecur);
+
+		if (events.length > 0) {
+			return events;
+		} 
+
+		return null;			
+	}
+});
+
+/*
+ * STANDARD
+ */
+
+var StandardProperties = [
+	_P("dtstart", 1, true), _P("tzoffsetto", 1, true), _P("tzoffsetfrom", 1, true),
+	_P("comment"), _P("rdate"), _P("rrule"), _P("tzname")
+];
+
+
+dojo.iCalendar.Standard = function (/* string */ body) {
+	// summary
+	// STANDARD Component
+
+	this.name = "STANDARD";
+	this._ValidProperties = StandardProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.Standard, dojo.iCalendar.Component);
+
+/*
+ * DAYLIGHT
+ */
+
+var DaylightProperties = [
+	_P("dtstart", 1, true), _P("tzoffsetto", 1, true), _P("tzoffsetfrom", 1, true),
+	_P("comment"), _P("rdate"), _P("rrule"), _P("tzname")
+];
+
+dojo.iCalendar.Daylight = function (/* string */ body) {
+	// summary
+	// Daylight Component
+	this.name = "DAYLIGHT";
+	this._ValidProperties = DaylightProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.Daylight, dojo.iCalendar.Component);
+
+/*
+ * VEVENT
+ */
+
+var VEventProperties = [
+	// these can occur once only
+	_P("class", 1), _P("created", 1), _P("description", 1), _P("dtstart", 1),
+	_P("geo", 1), _P("last-mod", 1), _P("location", 1), _P("organizer", 1),
+	_P("priority", 1), _P("dtstamp", 1), _P("seq", 1), _P("status", 1),
+	_P("summary", 1), _P("transp", 1), _P("uid", 1), _P("url", 1), _P("recurid", 1),
+	// these two are exclusive
+	[_P("dtend", 1), _P("duration", 1)],
+	// these can occur many times over
+	_P("attach"), _P("attendee"), _P("categories"), _P("comment"), _P("contact"),
+	_P("exdate"), _P("exrule"), _P("rstatus"), _P("related"), _P("resources"),
+	_P("rdate"), _P("rrule")
+];
+
+dojo.iCalendar.VEvent = function (/* string */ body) {
+	// summary 
+	// VEVENT Component
+	this._ValidProperties = VEventProperties;
+	this.name = "VEVENT";
+	dojo.iCalendar.Component.call(this, body);
+	this.recurring = false;
+	this.startDate = dojo.date.fromIso8601(this.dtstart.value);
+}
+
+dojo.inherits(dojo.iCalendar.VEvent, dojo.iCalendar.Component);
+
+dojo.extend(dojo.iCalendar.VEvent, {
+		getDates: function(until) {
+			var dtstart = this.getDate();
+
+			var recurranceSet = [];
+			var weekdays=["su","mo","tu","we","th","fr","sa"];
+			var order = { 
+				"daily": 1, "weekly": 2, "monthly": 3, "yearly": 4,
+				"byday": 1, "bymonthday": 1, "byweekno": 2, "bymonth": 3, "byyearday": 4};
+
+			// expand rrules into the recurrance 
+			for (var x=0; x<this.rrule.length; x++) {
+				var rrule = this.rrule[x];
+				var freq = rrule.freq.toLowerCase();
+				var interval = 1;
+
+				if (rrule.interval > interval) {
+					interval = rrule.interval;
+				}
+
+				var set = [];
+				var freqInt = order[freq];
+
+				if (rrule.until) {
+					var tmpUntil = dojo.date.fromIso8601(rrule.until);
+				} else {
+					var tmpUntil = until
+				}
+
+				if (tmpUntil > until) {
+					tmpUntil = until
+				}
+
+
+				if (dtstart<tmpUntil) {
+
+					var expandingRules = function(){};
+					var cullingRules = function(){};
+					expandingRules.length=0;
+					cullingRules.length =0;
+
+					switch(freq) {
+						case "yearly":
+							var nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setYear(nextDate.getFullYear()+interval);
+								tmpDate = new Date(nextDate);
+								if(tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;
+						case "monthly":
+							nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setMonth(nextDate.getMonth()+interval);
+								var tmpDate = new Date(nextDate);
+								if (tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;
+						case "weekly":
+							nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setDate(nextDate.getDate()+(7*interval));
+								var tmpDate = new Date(nextDate);
+								if (tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;	
+						case "daily":
+							nextDate = new Date(dtstart);
+							set.push(nextDate);
+							while(nextDate < tmpUntil) {
+								nextDate.setDate(nextDate.getDate()+interval);
+								var tmpDate = new Date(nextDate);
+								if (tmpDate < tmpUntil) {
+									set.push(tmpDate);
+								}
+							}
+							break;
+	
+					}
+
+					if ((rrule["bymonth"]) && (order["bymonth"]<freqInt))	{
+						for (var z=0; z<rrule["bymonth"].length; z++) {
+							if (z==0) {
+								for (var zz=0; zz < set.length; zz++) {
+									set[zz].setMonth(rrule["bymonth"][z]-1);
+								}
+							} else {
+								var subset=[];
+								for (var zz=0; zz < set.length; zz++) {
+									var newDate = new Date(set[zz]);
+									newDate.setMonth(rrule[z]);
+									subset.push(newDate);
+								}
+								tmp = set.concat(subset);
+								set = tmp;
+							}
+						}
+					}
+
+					
+					// while the spec doesn't prohibit it, it makes no sense to have a bymonth and a byweekno at the same time
+					// and if i'm wrong then i don't know how to apply that rule.  This is also documented elsewhere on the web
+					if (rrule["byweekno"] && !rrule["bymonth"]) {	
+						dojo.debug("TODO: no support for byweekno yet");
+					}
+
+
+					// while the spec doesn't prohibit it, it makes no sense to have a bymonth and a byweekno at the same time
+					// and if i'm wrong then i don't know how to apply that rule.  This is also documented elsewhere on the web
+					if (rrule["byyearday"] && !rrule["bymonth"] && !rrule["byweekno"] ) {	
+						if (rrule["byyearday"].length > 1) {
+							var regex = "([+-]?)([0-9]{1,3})";
+							for (var z=1; x<rrule["byyearday"].length; z++) {
+								var regexResult = rrule["byyearday"][z].match(regex);
+								if (z==1) {
+									for (var zz=0; zz < set.length; zz++) {
+										if (regexResult[1] == "-") {
+											dojo.date.setDayOfYear(set[zz],366-regexResult[2]);
+										} else {
+											dojo.date.setDayOfYear(set[zz],regexResult[2]);
+										}
+									}
+								}	else {
+									var subset=[];
+									for (var zz=0; zz < set.length; zz++) {
+										var newDate = new Date(set[zz]);
+										if (regexResult[1] == "-") {
+											dojo.date.setDayOfYear(newDate,366-regexResult[2]);
+										} else {
+											dojo.date.setDayOfYear(newDate,regexResult[2]);
+										}
+										subset.push(newDate);
+									}
+									tmp = set.concat(subset);
+									set = tmp;
+								}
+							}
+						}
+					}
+
+					if (rrule["bymonthday"]  && (order["bymonthday"]<freqInt)) {	
+						if (rrule["bymonthday"].length > 0) {
+							var regex = "([+-]?)([0-9]{1,3})";
+							for (var z=0; z<rrule["bymonthday"].length; z++) {
+								var regexResult = rrule["bymonthday"][z].match(regex);
+								if (z==0) {
+									for (var zz=0; zz < set.length; zz++) {
+										if (regexResult[1] == "-") {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												set[zz].setDate(dojo.date.getDaysInMonth(set[zz]) - regexResult[2]);
+											}
+										} else {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												set[zz].setDate(regexResult[2]);
+											}
+										}
+									}
+								}	else {
+									var subset=[];
+									for (var zz=0; zz < set.length; zz++) {
+										var newDate = new Date(set[zz]);
+										if (regexResult[1] == "-") {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												newDate.setDate(dojo.date.getDaysInMonth(set[zz]) - regexResult[2]);
+											}
+										} else {
+											if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+												newDate.setDate(regexResult[2]);
+											}
+										}
+										subset.push(newDate);
+									}
+									tmp = set.concat(subset);
+									set = tmp;
+								}
+							}
+						}
+					}
+
+					if (rrule["byday"]  && (order["byday"]<freqInt)) {	
+						if (rrule["bymonth"]) {
+							if (rrule["byday"].length > 0) {
+								var regex = "([+-]?)([0-9]{0,1}?)([A-Za-z]{1,2})";
+								for (var z=0; z<rrule["byday"].length; z++) {
+									var regexResult = rrule["byday"][z].match(regex);
+									var occurance = regexResult[2];
+									var day = regexResult[3].toLowerCase();
+
+
+									if (z==0) {
+										for (var zz=0; zz < set.length; zz++) {
+											if (regexResult[1] == "-") {
+												//find the nth to last occurance of date 
+												var numDaysFound = 0;
+												var lastDayOfMonth = dojo.date.getDaysInMonth(set[zz]);
+												var daysToSubtract = 1;
+												set[zz].setDate(lastDayOfMonth); 
+												if (weekdays[set[zz].getDay()] == day) {
+													numDaysFound++;
+													daysToSubtract=7;
+												}
+												daysToSubtract = 1;
+												while (numDaysFound < occurance) {
+													set[zz].setDate(set[zz].getDate()-daysToSubtract);	
+													if (weekdays[set[zz].getDay()] == day) {
+														numDaysFound++;
+														daysToSubtract=7;	
+													}
+												}
+											} else {
+												if (occurance) {
+													var numDaysFound=0;
+													set[zz].setDate(1);
+													var daysToAdd=1;
+
+													if(weekdays[set[zz].getDay()] == day) {
+														numDaysFound++;
+														daysToAdd=7;
+													}
+
+													while(numDaysFound < occurance) {
+														set[zz].setDate(set[zz].getDate()+daysToAdd);
+														if(weekdays[set[zz].getDay()] == day) {
+															numDaysFound++;
+															daysToAdd=7;
+														}
+													}
+												} else {
+													//we're gonna expand here to add a date for each of the specified days for each month
+													var numDaysFound=0;
+													var subset = [];
+
+													lastDayOfMonth = new Date(set[zz]);
+													var daysInMonth = dojo.date.getDaysInMonth(set[zz]);
+													lastDayOfMonth.setDate(daysInMonth);
+
+													set[zz].setDate(1);
+												
+													if (weekdays[set[zz].getDay()] == day) {
+														numDaysFound++;
+													}
+													var tmpDate = new Date(set[zz]);
+													daysToAdd = 1;
+													while(tmpDate.getDate() < lastDayOfMonth) {
+														if (weekdays[tmpDate.getDay()] == day) {
+															numDaysFound++;
+															if (numDaysFound==1) {
+																set[zz] = tmpDate;
+															} else {
+																subset.push(tmpDate);
+																tmpDate = new Date(tmpDate);
+																daysToAdd=7;	
+																tmpDate.setDate(tmpDate.getDate() + daysToAdd);
+															}
+														} else {
+															tmpDate.setDate(tmpDate.getDate() + daysToAdd);
+														}
+													}
+													var t = set.concat(subset);
+													set = t; 
+												}
+											}
+										}
+									}	else {
+										var subset=[];
+										for (var zz=0; zz < set.length; zz++) {
+											var newDate = new Date(set[zz]);
+											if (regexResult[1] == "-") {
+												if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+													newDate.setDate(dojo.date.getDaysInMonth(set[zz]) - regexResult[2]);
+												}
+											} else {
+												if (regexResult[2] < dojo.date.getDaysInMonth(set[zz])) {
+													newDate.setDate(regexResult[2]);
+												}
+											}
+											subset.push(newDate);
+										}
+										tmp = set.concat(subset);
+										set = tmp;
+									}
+								}
+							}
+						} else {
+							dojo.debug("TODO: byday within a yearly rule without a bymonth");
+						}
+					}
+
+					dojo.debug("TODO: Process BYrules for units larger than frequency");
+			
+					//add this set of events to the complete recurranceSet	
+					var tmp = recurranceSet.concat(set);
+					recurranceSet = tmp;
+				}
+			}
+
+			// TODO: add rdates to the recurrance set here
+
+			// TODO: subtract exdates from the recurrance set here
+
+			//TODO:  subtract dates generated by exrules from recurranceSet here
+
+			recurranceSet.push(dtstart);
+			return recurranceSet;
+		},
+
+		getDate: function() {
+			return dojo.date.fromIso8601(this.dtstart.value);
+		}
+});
+
+/*
+ * VTIMEZONE
+ */
+
+var VTimeZoneProperties = [
+	_P("tzid", 1, true), _P("last-mod", 1), _P("tzurl", 1)
+
+	// one of 'standardc' or 'daylightc' must occur
+	// and each may occur more than once.
+];
+
+dojo.iCalendar.VTimeZone = function (/* string */ body) {
+	// summary
+	// VTIMEZONE Component
+	this.name = "VTIMEZONE";
+	this._ValidProperties = VTimeZoneProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VTimeZone, dojo.iCalendar.Component);
+
+/*
+ * VTODO
+ */
+
+var VTodoProperties = [
+	// these can occur once only
+	_P("class", 1), _P("completed", 1), _P("created", 1), _P("description", 1),
+	_P("dtstart", 1), _P("geo", 1), _P("last-mod", 1), _P("location", 1),
+	_P("organizer", 1), _P("percent", 1), _P("priority", 1), _P("dtstamp", 1),
+	_P("seq", 1), _P("status", 1), _P("summary", 1), _P("uid", 1), _P("url", 1),
+	_P("recurid", 1),
+	// these two are exclusive
+	[_P("due", 1), _P("duration", 1)],
+	// these can occur many times over
+	_P("attach"), _P("attendee"), _P("categories"), _P("comment"), _P("contact"),
+	_P("exdate"), _P("exrule"), _P("rstatus"), _P("related"), _P("resources"),
+	_P("rdate"), _P("rrule")
+];
+
+dojo.iCalendar.VTodo= function (/* string */ body) {
+	// summary
+	// VTODO Componenet
+	this.name = "VTODO";
+	this._ValidProperties = VTodoProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VTodo, dojo.iCalendar.Component);
+
+/*
+ * VJOURNAL
+ */
+
+var VJournalProperties = [
+	// these can occur once only
+	_P("class", 1), _P("created", 1), _P("description", 1), _P("dtstart", 1),
+	_P("last-mod", 1), _P("organizer", 1), _P("dtstamp", 1), _P("seq", 1),
+	_P("status", 1), _P("summary", 1), _P("uid", 1), _P("url", 1), _P("recurid", 1),
+	// these can occur many times over
+	_P("attach"), _P("attendee"), _P("categories"), _P("comment"), _P("contact"),
+	_P("exdate"), _P("exrule"), _P("related"), _P("rstatus"), _P("rdate"), _P("rrule")
+];
+
+dojo.iCalendar.VJournal= function (/* string */ body) {
+	// summary
+	// VJOURNAL Component
+	this.name = "VJOURNAL";
+	this._ValidProperties = VJournalProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VJournal, dojo.iCalendar.Component);
+
+/*
+ * VFREEBUSY
+ */
+
+var VFreeBusyProperties = [
+	// these can occur once only
+	_P("contact"), _P("dtstart", 1), _P("dtend"), _P("duration"),
+	_P("organizer", 1), _P("dtstamp", 1), _P("uid", 1), _P("url", 1),
+	// these can occur many times over
+	_P("attendee"), _P("comment"), _P("freebusy"), _P("rstatus")
+];
+
+dojo.iCalendar.VFreeBusy= function (/* string */ body) {
+	// summary
+	// VFREEBUSY Component
+	this.name = "VFREEBUSY";
+	this._ValidProperties = VFreeBusyProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VFreeBusy, dojo.iCalendar.Component);
+
+/*
+ * VALARM
+ */
+
+var VAlarmProperties = [
+	[_P("action", 1, true), _P("trigger", 1, true), [_P("duration", 1), _P("repeat", 1)],
+	_P("attach", 1)],
+
+	[_P("action", 1, true), _P("description", 1, true), _P("trigger", 1, true),
+	[_P("duration", 1), _P("repeat", 1)]],
+
+	[_P("action", 1, true), _P("description", 1, true), _P("trigger", 1, true),
+	_P("summary", 1, true), _P("attendee", "*", true),
+	[_P("duration", 1), _P("repeat", 1)],
+	_P("attach", 1)],
+
+	[_P("action", 1, true), _P("attach", 1, true), _P("trigger", 1, true),
+	[_P("duration", 1), _P("repeat", 1)],
+	_P("description", 1)],
+];
+
+dojo.iCalendar.VAlarm= function (/* string */ body) {
+	// summary
+	// VALARM Component
+	this.name = "VALARM";
+	this._ValidProperties = VAlarmProperties;
+	dojo.iCalendar.Component.call(this, body);
+}
+
+dojo.inherits(dojo.iCalendar.VAlarm, dojo.iCalendar.Component);
+

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/iCalendar.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,14 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io");
+
+dojo.require("dojo.io.*");
+dojo.deprecated("dojo.io", "replaced by dojo.io.*", "0.5");

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/BrowserIO.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/BrowserIO.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/BrowserIO.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/BrowserIO.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,579 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.BrowserIO");
+
+dojo.require("dojo.io.common");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.string.extras");
+dojo.require("dojo.dom");
+dojo.require("dojo.undo.browser");
+
+dojo.io.checkChildrenForFile = function(node){
+	var hasFile = false;
+	var inputs = node.getElementsByTagName("input");
+	dojo.lang.forEach(inputs, function(input){
+		if(hasFile){ return; }
+		if(input.getAttribute("type")=="file"){
+			hasFile = true;
+		}
+	});
+	return hasFile;
+}
+
+dojo.io.formHasFile = function(formNode){
+	return dojo.io.checkChildrenForFile(formNode);
+}
+
+dojo.io.updateNode = function(node, urlOrArgs){
+	node = dojo.byId(node);
+	var args = urlOrArgs;
+	if(dojo.lang.isString(urlOrArgs)){
+		args = { url: urlOrArgs };
+	}
+	args.mimetype = "text/html";
+	args.load = function(t, d, e){
+		while(node.firstChild){
+			if(dojo["event"]){
+				try{
+					dojo.event.browser.clean(node.firstChild);
+				}catch(e){}
+			}
+			node.removeChild(node.firstChild);
+		}
+		node.innerHTML = d;
+	};
+	dojo.io.bind(args);
+}
+
+dojo.io.formFilter = function(node) {
+	var type = (node.type||"").toLowerCase();
+	return !node.disabled && node.name
+		&& !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type);
+}
+
+// TODO: Move to htmlUtils
+dojo.io.encodeForm = function(formNode, encoding, formFilter){
+	if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
+		dojo.raise("Attempted to encode a non-form element.");
+	}
+	if(!formFilter) { formFilter = dojo.io.formFilter; }
+	var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+	var values = [];
+
+	for(var i = 0; i < formNode.elements.length; i++){
+		var elm = formNode.elements[i];
+		if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
+		var name = enc(elm.name);
+		var type = elm.type.toLowerCase();
+
+		if(type == "select-multiple"){
+			for(var j = 0; j < elm.options.length; j++){
+				if(elm.options[j].selected) {
+					values.push(name + "=" + enc(elm.options[j].value));
+				}
+			}
+		}else if(dojo.lang.inArray(["radio", "checkbox"], type)){
+			if(elm.checked){
+				values.push(name + "=" + enc(elm.value));
+			}
+		}else{
+			values.push(name + "=" + enc(elm.value));
+		}
+	}
+
+	// now collect input type="image", which doesn't show up in the elements array
+	var inputs = formNode.getElementsByTagName("input");
+	for(var i = 0; i < inputs.length; i++) {
+		var input = inputs[i];
+		if(input.type.toLowerCase() == "image" && input.form == formNode
+			&& formFilter(input)) {
+			var name = enc(input.name);
+			values.push(name + "=" + enc(input.value));
+			values.push(name + ".x=0");
+			values.push(name + ".y=0");
+		}
+	}
+	return values.join("&") + "&";
+}
+
+dojo.io.FormBind = function(args) {
+	this.bindArgs = {};
+
+	if(args && args.formNode) {
+		this.init(args);
+	} else if(args) {
+		this.init({formNode: args});
+	}
+}
+dojo.lang.extend(dojo.io.FormBind, {
+	form: null,
+
+	bindArgs: null,
+
+	clickedButton: null,
+
+	init: function(args) {
+		var form = dojo.byId(args.formNode);
+
+		if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
+			throw new Error("FormBind: Couldn't apply, invalid form");
+		} else if(this.form == form) {
+			return;
+		} else if(this.form) {
+			throw new Error("FormBind: Already applied to a form");
+		}
+
+		dojo.lang.mixin(this.bindArgs, args);
+		this.form = form;
+
+		this.connect(form, "onsubmit", "submit");
+
+		for(var i = 0; i < form.elements.length; i++) {
+			var node = form.elements[i];
+			if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) {
+				this.connect(node, "onclick", "click");
+			}
+		}
+
+		var inputs = form.getElementsByTagName("input");
+		for(var i = 0; i < inputs.length; i++) {
+			var input = inputs[i];
+			if(input.type.toLowerCase() == "image" && input.form == form) {
+				this.connect(input, "onclick", "click");
+			}
+		}
+	},
+
+	onSubmit: function(form) {
+		return true;
+	},
+
+	submit: function(e) {
+		e.preventDefault();
+		if(this.onSubmit(this.form)) {
+			dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
+				formFilter: dojo.lang.hitch(this, "formFilter")
+			}));
+		}
+	},
+
+	click: function(e) {
+		var node = e.currentTarget;
+		if(node.disabled) { return; }
+		this.clickedButton = node;
+	},
+
+	formFilter: function(node) {
+		var type = (node.type||"").toLowerCase();
+		var accept = false;
+		if(node.disabled || !node.name) {
+			accept = false;
+		} else if(dojo.lang.inArray(["submit", "button", "image"], type)) {
+			if(!this.clickedButton) { this.clickedButton = node; }
+			accept = node == this.clickedButton;
+		} else {
+			accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type);
+		}
+		return accept;
+	},
+
+	// in case you don't have dojo.event.* pulled in
+	connect: function(srcObj, srcFcn, targetFcn) {
+		if(dojo.evalObjPath("dojo.event.connect")) {
+			dojo.event.connect(srcObj, srcFcn, this, targetFcn);
+		} else {
+			var fcn = dojo.lang.hitch(this, targetFcn);
+			srcObj[srcFcn] = function(e) {
+				if(!e) { e = window.event; }
+				if(!e.currentTarget) { e.currentTarget = e.srcElement; }
+				if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
+				fcn(e);
+			}
+		}
+	}
+});
+
+dojo.io.XMLHTTPTransport = new function(){
+	var _this = this;
+
+	var _cache = {}; // FIXME: make this public? do we even need to?
+	this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
+	this.preventCache = false; // if this is true, we'll always force GET requests to cache
+
+	// FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
+	function getCacheKey(url, query, method) {
+		return url + "|" + query + "|" + method.toLowerCase();
+	}
+
+	function addToCache(url, query, method, http) {
+		_cache[getCacheKey(url, query, method)] = http;
+	}
+
+	function getFromCache(url, query, method) {
+		return _cache[getCacheKey(url, query, method)];
+	}
+
+	this.clearCache = function() {
+		_cache = {};
+	}
+
+	// moved successful load stuff here
+	function doLoad(kwArgs, http, url, query, useCache) {
+		if(	((http.status>=200)&&(http.status<300))|| 	// allow any 2XX response code
+			(http.status==304)|| 						// get it out of the cache
+			(location.protocol=="file:" && (http.status==0 || http.status==undefined))||
+			(location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
+		){
+			var ret;
+			if(kwArgs.method.toLowerCase() == "head"){
+				var headers = http.getAllResponseHeaders();
+				ret = {};
+				ret.toString = function(){ return headers; }
+				var values = headers.split(/[\r\n]+/g);
+				for(var i = 0; i < values.length; i++) {
+					var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
+					if(pair) {
+						ret[pair[1]] = pair[2];
+					}
+				}
+			}else if(kwArgs.mimetype == "text/javascript"){
+				try{
+					ret = dj_eval(http.responseText);
+				}catch(e){
+					dojo.debug(e);
+					dojo.debug(http.responseText);
+					ret = null;
+				}
+			}else if(kwArgs.mimetype == "text/json"){
+				try{
+					ret = dj_eval("("+http.responseText+")");
+				}catch(e){
+					dojo.debug(e);
+					dojo.debug(http.responseText);
+					ret = false;
+				}
+			}else if((kwArgs.mimetype == "application/xml")||
+						(kwArgs.mimetype == "text/xml")){
+				ret = http.responseXML;
+				if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
+					ret = dojo.dom.createDocumentFromText(http.responseText);
+				}
+			}else{
+				ret = http.responseText;
+			}
+
+			if(useCache){ // only cache successful responses
+				addToCache(url, query, kwArgs.method, http);
+			}
+			kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
+		}else{
+			var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
+			kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
+		}
+	}
+
+	// set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
+	function setHeaders(http, kwArgs){
+		if(kwArgs["headers"]) {
+			for(var header in kwArgs["headers"]) {
+				if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
+					kwArgs["contentType"] = kwArgs["headers"][header];
+				} else {
+					http.setRequestHeader(header, kwArgs["headers"][header]);
+				}
+			}
+		}
+	}
+
+	this.inFlight = [];
+	this.inFlightTimer = null;
+
+	this.startWatchingInFlight = function(){
+		if(!this.inFlightTimer){
+			// setInterval broken in mozilla x86_64 in some circumstances, see
+			// https://bugzilla.mozilla.org/show_bug.cgi?id=344439
+			// using setTimeout instead
+			this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
+		}
+	}
+
+	this.watchInFlight = function(){
+		var now = null;
+		// make sure sync calls stay thread safe, if this callback is called during a sync call
+		// and this results in another sync call before the first sync call ends the browser hangs
+		if(!dojo.hostenv._blockAsync && !_this._blockAsync){
+			for(var x=this.inFlight.length-1; x>=0; x--){
+				var tif = this.inFlight[x];
+				if(!tif || tif.http._aborted || !tif.http.readyState){
+					this.inFlight.splice(x, 1); continue; 
+				}
+				if(4==tif.http.readyState){
+					// remove it so we can clean refs
+					this.inFlight.splice(x, 1);
+					doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
+				}else if (tif.startTime){
+					//See if this is a timeout case.
+					if(!now){
+						now = (new Date()).getTime();
+					}
+					if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
+						//Stop the request.
+						if(typeof tif.http.abort == "function"){
+							tif.http.abort();
+						}
+	
+						// remove it so we can clean refs
+						this.inFlight.splice(x, 1);
+						tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
+					}
+				}
+			}
+		}
+
+		clearTimeout(this.inFlightTimer);
+		if(this.inFlight.length == 0){
+			this.inFlightTimer = null;
+			return;
+		}
+		this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
+	}
+
+	var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
+	this.canHandle = function(kwArgs){
+		// canHandle just tells dojo.io.bind() if this is a good transport to
+		// use for the particular type of request.
+
+		// FIXME: we need to determine when form values need to be
+		// multi-part mime encoded and avoid using this transport for those
+		// requests.
+		return hasXmlHttp
+			&& dojo.lang.inArray(["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json"], (kwArgs["mimetype"].toLowerCase()||""))
+			&& !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) );
+	}
+
+	this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F";	// unique guid as a boundary value for multipart posts
+
+	this.bind = function(kwArgs){
+		if(!kwArgs["url"]){
+			// are we performing a history action?
+			if( !kwArgs["formNode"]
+				&& (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
+				&& (!djConfig.preventBackButtonFix)) {
+        dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
+        				"Use dojo.undo.browser.addToHistory() instead.", "0.4");
+				dojo.undo.browser.addToHistory(kwArgs);
+				return true;
+			}
+		}
+
+		// build this first for cache purposes
+		var url = kwArgs.url;
+		var query = "";
+		if(kwArgs["formNode"]){
+			var ta = kwArgs.formNode.getAttribute("action");
+			if((ta)&&(!kwArgs["url"])){ url = ta; }
+			var tp = kwArgs.formNode.getAttribute("method");
+			if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
+			query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
+		}
+
+		if(url.indexOf("#") > -1) {
+			dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
+			url = url.split("#")[0];
+		}
+
+		if(kwArgs["file"]){
+			// force post for file transfer
+			kwArgs.method = "post";
+		}
+
+		if(!kwArgs["method"]){
+			kwArgs.method = "get";
+		}
+
+		// guess the multipart value
+		if(kwArgs.method.toLowerCase() == "get"){
+			// GET cannot use multipart
+			kwArgs.multipart = false;
+		}else{
+			if(kwArgs["file"]){
+				// enforce multipart when sending files
+				kwArgs.multipart = true;
+			}else if(!kwArgs["multipart"]){
+				// default 
+				kwArgs.multipart = false;
+			}
+		}
+
+		if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
+			dojo.undo.browser.addToHistory(kwArgs);
+		}
+
+		var content = kwArgs["content"] || {};
+
+		if(kwArgs.sendTransport) {
+			content["dojo.transport"] = "xmlhttp";
+		}
+
+		do { // break-block
+			if(kwArgs.postContent){
+				query = kwArgs.postContent;
+				break;
+			}
+
+			if(content) {
+				query += dojo.io.argsFromMap(content, kwArgs.encoding);
+			}
+			
+			if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
+				break;
+			}
+
+			var	t = [];
+			if(query.length){
+				var q = query.split("&");
+				for(var i = 0; i < q.length; ++i){
+					if(q[i].length){
+						var p = q[i].split("=");
+						t.push(	"--" + this.multipartBoundary,
+								"Content-Disposition: form-data; name=\"" + p[0] + "\"", 
+								"",
+								p[1]);
+					}
+				}
+			}
+
+			if(kwArgs.file){
+				if(dojo.lang.isArray(kwArgs.file)){
+					for(var i = 0; i < kwArgs.file.length; ++i){
+						var o = kwArgs.file[i];
+						t.push(	"--" + this.multipartBoundary,
+								"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
+								"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
+								"",
+								o.content);
+					}
+				}else{
+					var o = kwArgs.file;
+					t.push(	"--" + this.multipartBoundary,
+							"Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
+							"Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
+							"",
+							o.content);
+				}
+			}
+
+			if(t.length){
+				t.push("--"+this.multipartBoundary+"--", "");
+				query = t.join("\r\n");
+			}
+		}while(false);
+
+		// kwArgs.Connection = "close";
+
+		var async = kwArgs["sync"] ? false : true;
+
+		var preventCache = kwArgs["preventCache"] ||
+			(this.preventCache == true && kwArgs["preventCache"] != false);
+		var useCache = kwArgs["useCache"] == true ||
+			(this.useCache == true && kwArgs["useCache"] != false );
+
+		// preventCache is browser-level (add query string junk), useCache
+		// is for the local cache. If we say preventCache, then don't attempt
+		// to look in the cache, but if useCache is true, we still want to cache
+		// the response
+		if(!preventCache && useCache){
+			var cachedHttp = getFromCache(url, query, kwArgs.method);
+			if(cachedHttp){
+				doLoad(kwArgs, cachedHttp, url, query, false);
+				return;
+			}
+		}
+
+		// much of this is from getText, but reproduced here because we need
+		// more flexibility
+		var http = dojo.hostenv.getXmlhttpObject(kwArgs);	
+		var received = false;
+
+		// build a handler function that calls back to the handler obj
+		if(async){
+			var startTime = 
+			// FIXME: setting up this callback handler leaks on IE!!!
+			this.inFlight.push({
+				"req":		kwArgs,
+				"http":		http,
+				"url":	 	url,
+				"query":	query,
+				"useCache":	useCache,
+				"startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
+			});
+			this.startWatchingInFlight();
+		}else{
+			// block async callbacks until sync is in, needed in khtml, others?
+			_this._blockAsync = true;
+		}
+
+		if(kwArgs.method.toLowerCase() == "post"){
+			// FIXME: need to hack in more flexible Content-Type setting here!
+			if (!kwArgs.usr) {
+				http.open("POST", url, async);
+			}else{
+        http.open("POST", url, async, kwArgs.usr, kwArgs.passwd);
+			}
+			setHeaders(http, kwArgs);
+			http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) : 
+				(kwArgs.contentType || "application/x-www-form-urlencoded"));
+			try{
+				http.send(query);
+			}catch(e){
+				if(typeof http.abort == "function"){
+					http.abort();
+				}
+				doLoad(kwArgs, {status: 404}, url, query, useCache);
+			}
+		}else{
+			var tmpUrl = url;
+			if(query != "") {
+				tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
+			}
+			if(preventCache) {
+				tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
+					? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
+			}
+			http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
+			setHeaders(http, kwArgs);
+			try {
+				http.send(null);
+			}catch(e)	{
+				if(typeof http.abort == "function"){
+					http.abort();
+				}
+				doLoad(kwArgs, {status: 404}, url, query, useCache);
+			}
+		}
+
+		if( !async ) {
+			doLoad(kwArgs, http, url, query, useCache);
+			_this._blockAsync = false;
+		}
+
+		kwArgs.abort = function(){
+			try{// khtml doesent reset readyState on abort, need this workaround
+				http._aborted = true; 
+			}catch(e){/*squelsh*/}
+			return http.abort();
+		}
+
+		return;
+	}
+	dojo.io.transports.addTransport("XMLHTTPTransport");
+}

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/BrowserIO.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/IframeIO.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/IframeIO.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/IframeIO.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/IframeIO.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,246 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.IframeIO");
+dojo.require("dojo.io.BrowserIO");
+dojo.require("dojo.uri.*");
+
+// FIXME: is it possible to use the Google htmlfile hack to prevent the
+// background click with this transport?
+
+dojo.io.createIFrame = function(fname, onloadstr, uri){
+	if(window[fname]){ return window[fname]; }
+	if(window.frames[fname]){ return window.frames[fname]; }
+	var r = dojo.render.html;
+	var cframe = null;
+	var turi = uri||dojo.uri.dojoUri("iframe_history.html?noInit=true");
+	var ifrstr = ((r.ie)&&(dojo.render.os.win)) ? '<iframe name="'+fname+'" src="'+turi+'" onload="'+onloadstr+'">' : 'iframe';
+	cframe = document.createElement(ifrstr);
+	with(cframe){
+		name = fname;
+		setAttribute("name", fname);
+		id = fname;
+	}
+	dojo.body().appendChild(cframe);
+	window[fname] = cframe;
+
+	with(cframe.style){
+		if(!r.safari){
+			//We can't change the src in Safari 2.0.3 if absolute position. Bizarro.
+			position = "absolute";
+		}
+		left = top = "0px";
+		height = width = "1px";
+		visibility = "hidden";
+		/*
+		if(djConfig.isDebug){
+			position = "relative";
+			height = "300px";
+			width = "600px";
+			visibility = "visible";
+		}
+		*/
+	}
+
+	if(!r.ie){
+		dojo.io.setIFrameSrc(cframe, turi, true);
+		cframe.onload = new Function(onloadstr);
+	}
+	
+	return cframe;
+}
+
+dojo.io.IframeTransport = new function(){
+	var _this = this;
+	this.currentRequest = null;
+	this.requestQueue = [];
+	this.iframeName = "dojoIoIframe";
+
+	this.fireNextRequest = function(){
+		if((this.currentRequest)||(this.requestQueue.length == 0)){ return; }
+		// dojo.debug("fireNextRequest");
+		var cr = this.currentRequest = this.requestQueue.shift();
+		cr._contentToClean = [];
+		var fn = cr["formNode"];
+		var content = cr["content"] || {};
+		if(cr.sendTransport) {
+			content["dojo.transport"] = "iframe";
+		}
+		if(fn){
+			if(content){
+				// if we have things in content, we need to add them to the form
+				// before submission
+				for(var x in content){
+					if(!fn[x]){
+						var tn;
+						if(dojo.render.html.ie){
+							tn = document.createElement("<input type='hidden' name='"+x+"' value='"+content[x]+"'>");
+							fn.appendChild(tn);
+						}else{
+							tn = document.createElement("input");
+							fn.appendChild(tn);
+							tn.type = "hidden";
+							tn.name = x;
+							tn.value = content[x];
+						}
+						cr._contentToClean.push(x);
+					}else{
+						fn[x].value = content[x];
+					}
+				}
+			}
+			if(cr["url"]){
+				cr._originalAction = fn.getAttribute("action");
+				fn.setAttribute("action", cr.url);
+			}
+			if(!fn.getAttribute("method")){
+				fn.setAttribute("method", (cr["method"]) ? cr["method"] : "post");
+			}
+			cr._originalTarget = fn.getAttribute("target");
+			fn.setAttribute("target", this.iframeName);
+			fn.target = this.iframeName;
+			fn.submit();
+		}else{
+			// otherwise we post a GET string by changing URL location for the
+			// iframe
+			var query = dojo.io.argsFromMap(this.currentRequest.content);
+			var tmpUrl = (cr.url.indexOf("?") > -1 ? "&" : "?") + query;
+			dojo.io.setIFrameSrc(this.iframe, tmpUrl, true);
+		}
+	}
+
+	this.canHandle = function(kwArgs){
+		return (
+			(
+				// FIXME: can we really handle text/plain and
+				// text/javascript requests?
+				dojo.lang.inArray([	"text/plain", "text/html", "text/javascript", "text/json"], kwArgs["mimetype"])
+			)&&(
+				// make sur we really only get used in file upload cases	
+				(kwArgs["formNode"])&&(dojo.io.checkChildrenForFile(kwArgs["formNode"]))
+			)&&(
+				dojo.lang.inArray(["post", "get"], kwArgs["method"].toLowerCase())
+			)&&(
+				// never handle a sync request
+				!  ((kwArgs["sync"])&&(kwArgs["sync"] == true))
+			)
+		);
+	}
+
+	this.bind = function(kwArgs){
+		if(!this["iframe"]){ this.setUpIframe(); }
+		this.requestQueue.push(kwArgs);
+		this.fireNextRequest();
+		return;
+	}
+
+	this.setUpIframe = function(){
+
+		// NOTE: IE 5.0 and earlier Mozilla's don't support an onload event for
+		//       iframes. OTOH, we don't care.
+		this.iframe = dojo.io.createIFrame(this.iframeName, "dojo.io.IframeTransport.iframeOnload();");
+	}
+
+	this.iframeOnload = function(){
+		if(!_this.currentRequest){
+			_this.fireNextRequest();
+			return;
+		}
+
+		var req = _this.currentRequest;
+
+		// remove all the hidden content inputs
+		var toClean = req._contentToClean;
+		for(var i = 0; i < toClean.length; i++) {
+			var key = toClean[i];
+			if(dojo.render.html.safari){
+				//In Safari (at least 2.0.3), can't use formNode[key] syntax to find the node,
+				//for nodes that were dynamically added.
+				var fNode = req.formNode;
+				for(var j = 0; j < fNode.childNodes.length; j++){
+					var chNode = fNode.childNodes[j];
+					if(chNode.name == key){
+						var pNode = chNode.parentNode;
+						pNode.removeChild(chNode);
+						break;
+					}
+				}
+			}else if(req.formNode){
+				var input = req.formNode[key];
+				req.formNode.removeChild(input);
+				req.formNode[key] = null;
+			}
+		}
+
+		// restore original action + target
+		if(req["_originalAction"]){
+			req.formNode.setAttribute("action", req._originalAction);
+		}
+		req.formNode.setAttribute("target", req._originalTarget);
+		req.formNode.target = req._originalTarget;
+
+		var contentDoc = function(iframe_el){
+			var doc = iframe_el.contentDocument || // W3
+				(
+					(iframe_el.contentWindow)&&(iframe_el.contentWindow.document)
+				) ||  // IE
+				(
+					(iframe_el.name)&&(document.frames[iframe_el.name])&&
+					(document.frames[iframe_el.name].document)
+				) || null;
+			return doc;
+		};
+
+		var ifd = contentDoc(_this.iframe);
+		// handle successful returns
+		// FIXME: how do we determine success for iframes? Is there an equiv of
+		// the "status" property?
+		var value;
+		var success = false;
+
+		try{
+			var cmt = req.mimetype;
+			if((cmt == "text/javascript")||(cmt == "text/json")){
+				// FIXME: not sure what to do here? try to pull some evalulable
+				// text from a textarea or cdata section? 
+				// how should we set up the contract for that?
+				var js = ifd.getElementsByTagName("textarea")[0].value;
+				if(cmt == "text/json") { js = "(" + js + ")"; }
+				value = dj_eval(js);
+			}else if(cmt == "text/html"){
+				value = ifd;
+			}else{ // text/plain
+				value = ifd.getElementsByTagName("textarea")[0].value;
+			}
+			success = true;
+		}catch(e){ 
+			// looks like we didn't get what we wanted!
+			var errObj = new dojo.io.Error("IframeTransport Error");
+			if(dojo.lang.isFunction(req["error"])){
+				req.error("error", errObj, req);
+			}
+		}
+
+		// don't want to mix load function errors with processing errors, thus
+		// a separate try..catch
+		try {
+			if(success && dojo.lang.isFunction(req["load"])){
+				req.load("load", value, req);
+			}
+		} catch(e) {
+			throw e;
+		} finally {
+			_this.currentRequest = null;
+			_this.fireNextRequest();
+		}
+	}
+
+	dojo.io.transports.addTransport("IframeTransport");
+}

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/IframeIO.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RepubsubIO.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RepubsubIO.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RepubsubIO.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RepubsubIO.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,516 @@
+//	Copyright (c) 2004 Friendster Inc., Licensed under the Academic Free
+//	License version 2.0 or later 
+
+dojo.require("dojo.event.*");
+dojo.require("dojo.io.BrowserIO");
+
+dojo.provide("dojo.io.RepubsubIO");
+dojo.provide("dojo.io.repubsub");
+dojo.provide("dojo.io.repubsubTransport");
+
+dojo.io.repubsubTranport = new function(){
+	var rps = dojo.io.repubsub;
+	this.canHandle = function(kwArgs){
+		if((kwArgs["mimetype"] == "text/javascript")&&(kwArgs["method"] == "repubsub")){
+			return true;
+		}
+		return false;
+	}
+
+	this.bind = function(kwArgs){
+		if(!rps.isInitialized){
+			// open up our tunnel, queue up requests anyway
+			rps.init();
+		}
+		// FIXME: we need to turn this into a topic subscription
+		// var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
+		// sampleTransport.sendRequest(tgtURL, hdlrFunc);
+
+		// a normal "bind()" call in a request-response transport layer is
+		// something that (usually) encodes most of it's payload with the
+		// request. Multi-event systems like repubsub are a bit more complex,
+		// and repubsub in particular distinguishes the publish and subscribe
+		// portions of thep rocess with different method calls to handle each.
+		// Therefore, a "bind" in the sense of repubsub must first determine if
+		// we have an open subscription to a channel provided by the server,
+		// and then "publish" the request payload if there is any. We therefore
+		// must take care not to incorrectly or too agressively register or
+		// file event handlers which are provided with the kwArgs method.
+
+		// NOTE: we ONLY pay attention to those event handlers that are
+		// registered with the bind request that subscribes to the channel. If
+		// event handlers are provided with subsequent requests, we might in
+		// the future support some additive or replacement syntax, but for now
+		// they get dropped on the floor.
+
+		// NOTE: in this case, url MUST be the "topic" to which we
+		// subscribe/publish for this channel
+		if(!rps.topics[kwArgs.url]){
+			kwArgs.rpsLoad = function(evt){
+				kwArgs.load("load", evt);
+			}
+			rps.subscribe(kwArgs.url, kwArgs, "rpsLoad");
+		}
+
+		if(kwArgs["content"]){
+			// what we wanted to send
+			var cEvt = dojo.io.repubsubEvent.initFromProperties(kwArgs.content);
+			rps.publish(kwArgs.url, cEvt);
+		}
+	}
+
+	dojo.io.transports.addTransport("repubsubTranport");
+}
+
+dojo.io.repubsub = new function(){
+	this.initDoc = "init.html";
+	this.isInitialized = false;
+	this.subscriptionBacklog = [];
+	this.debug = true;
+	this.rcvNodeName = null;
+	this.sndNodeName = null;
+	this.rcvNode = null;
+	this.sndNode = null;
+	this.canRcv = false;
+	this.canSnd = false;
+	this.canLog = false;
+	this.sndTimer = null;
+	this.windowRef = window;
+	this.backlog = [];
+	this.tunnelInitCount = 0;
+	this.tunnelFrameKey = "tunnel_frame";
+	this.serverBaseURL = location.protocol+"//"+location.host+location.pathname;
+	this.logBacklog = [];
+	this.getRandStr = function(){
+		return Math.random().toString().substring(2, 10);
+	}
+	this.userid = "guest";
+	this.tunnelID = this.getRandStr();
+	this.attachPathList = [];
+	this.topics = []; // list of topics we have listeners to
+
+	// actually, now that I think about it a little bit more, it would sure be
+	// useful to parse out the <script> src attributes. We're looking for
+	// something with a "do_method=lib", since that's what would have included
+	// us in the first place (in the common case).
+	this.parseGetStr = function(){
+		var baseUrl = document.location.toString();
+		var params = baseUrl.split("?", 2);
+		if(params.length > 1){
+			var paramStr = params[1];
+			var pairs = paramStr.split("&");
+			var opts = [];
+			for(var x in pairs){
+				var sp = pairs[x].split("=");
+				// FIXME: is this eval dangerous?
+				try{
+					opts[sp[0]]=eval(sp[1]);
+				}catch(e){
+					opts[sp[0]]=sp[1];
+				}
+			}
+			return opts;
+		}else{
+			return [];
+		}
+	}
+
+	// parse URL params and use them as default vals
+	var getOpts = this.parseGetStr();
+	for(var x in getOpts){
+		// FIXME: should I be checking for undefined here before setting? Does
+		//        that buy me anything?
+		this[x] = getOpts[x];
+	}
+
+	if(!this["tunnelURI"]){
+		this.tunnelURI = [	"/who/", escape(this.userid), "/s/", 
+							this.getRandStr(), "/kn_journal"].join("");
+		// this.tunnelURI = this.absoluteTopicURI(this.tunnelURI);
+	}
+
+	/*
+	if (self.kn_tunnelID) kn.tunnelID = self.kn_tunnelID; // the server says
+	if (kn._argv.kn_tunnelID) kn.tunnelID = kn._argv.kn_tunnelID; // the url says
+	*/
+
+	// check the options object if it exists and use its properties as an
+	// over-ride
+	if(window["repubsubOpts"]||window["rpsOpts"]){
+		var optObj = window["repubsubOpts"]||window["rpsOpts"];
+		for(var x in optObj){
+			this[x] = optObj[x]; // copy the option object properties
+		}
+	}
+
+	// things that get called directly from our iframe to inform us of events
+	this.tunnelCloseCallback = function(){
+		// when we get this callback, we should immediately attempt to re-start
+		// our tunnel connection
+		dojo.io.setIFrameSrc(this.rcvNode, this.initDoc+"?callback=repubsub.rcvNodeReady&domain="+document.domain);
+	}
+
+	this.receiveEventFromTunnel = function(evt, srcWindow){
+		// we should never be getting events from windows we didn't create
+		// NOTE: events sourced from the local window are also supported for
+		// 		 debugging purposes
+
+		// any event object MUST have a an "elements" property
+		if(!evt["elements"]){
+			this.log("bailing! event received without elements!", "error");
+			return;
+		}
+
+		// if the event passes some minimal sanity tests, we need to attempt to
+		// dispatch it!
+
+		// first, it seems we have to munge the event object a bit
+		var e = {};
+		for(var i=0; i<evt.elements.length; i++){
+			var ee = evt.elements[i];
+			e[ee.name||ee.nameU] = (ee.value||ee.valueU);
+			// FIXME: need to enable this only in some extreme debugging mode!
+			this.log("[event]: "+(ee.name||ee.nameU)+": "+e[ee.name||ee.nameU]);
+		}
+
+		// NOTE: the previous version of this library put a bunch of code here
+		// to manage state that tried to make sure that we never, ever, lost
+		// any info about an event. If we unload RIGHT HERE, I don't think it's
+		// going to make a huge difference one way or another. Time will tell.
+
+		// and with THAT out of the way, dispatch it!
+		this.dispatch(e);
+
+		// TODO: remove the script block that created the event obj to save
+		// memory, etc.
+	}
+
+	this.widenDomain = function(domainStr){
+		// the purpose of this is to set the most liberal domain policy
+		// available
+		var cd = domainStr||document.domain;
+		if(cd.indexOf(".")==-1){ return; } // probably file:/// or localhost
+		var dps = cd.split(".");
+		if(dps.length<=2){ return; } // probably file:/// or an RFC 1918 address
+		dps = dps.slice(dps.length-2);
+		document.domain = dps.join(".");
+	}
+
+	// FIXME: parseCookie and setCookie should be methods that are more broadly
+	// available. Perhaps in htmlUtils?
+
+	this.parseCookie = function(){
+		var cs = document.cookie;
+		var keypairs = cs.split(";");
+		for(var x=0; x<keypairs.length; x++){
+			keypairs[x] = keypairs[x].split("=");
+			if(x!=keypairs.length-1){ cs+=";"; }
+		}
+		return keypairs;
+	}
+
+	this.setCookie = function(keypairs, clobber){
+		// NOTE: we want to only ever set session cookies, so never provide an
+		// 		 expires date
+		if((clobber)&&(clobber==true)){ document.cookie = ""; }
+		var cs = "";
+		for(var x=0; x<keypairs.length; x++){
+			cs += keypairs[x][0]+"="+keypairs[x][1];
+			if(x!=keypairs.length-1){ cs+=";"; }
+		}
+		document.cookie = cs;
+	}
+
+	// FIXME: need to replace w/ dojo.log.*
+	this.log = function(str, lvl){
+		if(!this.debug){ return; } // we of course only care if we're in debug mode
+		while(this.logBacklog.length>0){
+			if(!this.canLog){ break; }
+			var blo = this.logBacklog.shift();
+			this.writeLog("["+blo[0]+"]: "+blo[1], blo[2]);
+		}
+		this.writeLog(str, lvl);
+	}
+
+	this.writeLog = function(str, lvl){
+		dojo.debug(((new Date()).toLocaleTimeString())+": "+str);
+	}
+
+	this.init = function(){
+		this.widenDomain();
+		// this.findPeers();
+		this.openTunnel();
+		this.isInitialized = true;
+		// FIXME: this seems like entirely the wrong place to replay the backlog
+		while(this.subscriptionBacklog.length){
+			this.subscribe.apply(this, this.subscriptionBacklog.shift());
+		}
+	}
+
+	this.clobber = function(){
+		if(this.rcvNode){
+			this.setCookie( [
+					[this.tunnelFrameKey,"closed"],
+					["path","/"]
+				], false 
+			);
+		}
+	}
+
+	this.openTunnel = function(){
+		// We create two iframes here:
+
+		// one for getting data
+		this.rcvNodeName = "rcvIFrame_"+this.getRandStr();
+		// set cookie that can be used to find the receiving iframe
+		this.setCookie( [
+				[this.tunnelFrameKey,this.rcvNodeName],
+				["path","/"]
+			], false
+		);
+
+		this.rcvNode = dojo.io.createIFrame(this.rcvNodeName);
+		// FIXME: set the src attribute here to the initialization URL
+		dojo.io.setIFrameSrc(this.rcvNode, this.initDoc+"?callback=repubsub.rcvNodeReady&domain="+document.domain);
+
+		// the other for posting data in reply
+
+		this.sndNodeName = "sndIFrame_"+this.getRandStr();
+		this.sndNode = dojo.io.createIFrame(this.sndNodeName);
+		// FIXME: set the src attribute here to the initialization URL
+		dojo.io.setIFrameSrc(this.sndNode, this.initDoc+"?callback=repubsub.sndNodeReady&domain="+document.domain);
+
+	}
+
+	this.rcvNodeReady = function(){
+		// FIXME: why is this sequence number needed? Why isn't the UID gen
+		// 		  function enough?
+        var statusURI = [this.tunnelURI, '/kn_status/', this.getRandStr(), '_', 
+						 String(this.tunnelInitCount++)].join(""); 
+            // (kn._seqNum++); // FIXME: !!!!
+		// this.canRcv = true;
+		this.log("rcvNodeReady");
+		// FIXME: initialize receiver and request the base topic
+		// dojo.io.setIFrameSrc(this.rcvNode, this.serverBaseURL+"/kn?do_method=blank");
+		var initURIArr = [	this.serverBaseURL, "/kn?kn_from=", escape(this.tunnelURI),
+							"&kn_id=", escape(this.tunnelID), "&kn_status_from=", 
+							escape(statusURI)];
+		// FIXME: does the above really need a kn_response_flush? won't the
+		// 		  server already know? If not, what good is it anyway?
+		dojo.io.setIFrameSrc(this.rcvNode, initURIArr.join(""));
+
+		// setup a status path listener, but don't tell the server about it,
+		// since it already knows we're itnerested in our own tunnel status
+		this.subscribe(statusURI, this, "statusListener", true);
+
+		this.log(initURIArr.join(""));
+	}
+
+	this.sndNodeReady = function(){
+		this.canSnd = true;
+		this.log("sndNodeReady");
+		this.log(this.backlog.length);
+		// FIXME: handle any pent-up send commands
+		if(this.backlog.length > 0){
+			this.dequeueEvent();
+		}
+	}
+
+	this.statusListener = function(evt){
+		this.log("status listener called");
+		this.log(evt.status, "info");
+	}
+
+	// this handles local event propigation
+	this.dispatch = function(evt){
+		// figure out what topic it came from
+		if(evt["to"]||evt["kn_routed_from"]){
+			var rf = evt["to"]||evt["kn_routed_from"];
+			// split off the base server URL
+			var topic = rf.split(this.serverBaseURL, 2)[1];
+			if(!topic){
+				// FIXME: how do we recover when we don't get a sane "from"? Do
+				// we try to route to it anyway?
+				topic = rf;
+			}
+			this.log("[topic] "+topic);
+			if(topic.length>3){
+				if(topic.slice(0, 3)=="/kn"){
+					topic = topic.slice(3);
+				}
+			}
+			if(this.attachPathList[topic]){
+				this.attachPathList[topic](evt);
+			}
+		}
+	}
+
+	this.subscribe = function(	topic /* kn_from in the old terminilogy */, 
+								toObj, toFunc, dontTellServer){
+		if(!this.isInitialized){
+			this.subscriptionBacklog.push([topic, toObj, toFunc, dontTellServer]);
+			return;
+		}
+		if(!this.attachPathList[topic]){
+			this.attachPathList[topic] = function(){ return true; }
+			this.log("subscribing to: "+topic);
+			this.topics.push(topic);
+		}
+		var revt = new dojo.io.repubsubEvent(this.tunnelURI, topic, "route");
+		var rstr = [this.serverBaseURL+"/kn", revt.toGetString()].join("");
+		dojo.event.kwConnect({
+			once: true,
+			srcObj: this.attachPathList, 
+			srcFunc: topic, 
+			adviceObj: toObj, 
+			adviceFunc: toFunc
+		});
+		// NOTE: the above is a local mapping, if we're not the leader, we
+		// 		 should connect our mapping to the topic handler of the peer
+		// 		 leader, this ensures that not matter what happens to the
+		// 		 leader, we don't really loose our heads if/when the leader
+		// 		 goes away.
+		if(!this.rcvNode){ /* this should be an error! */ }
+		if(dontTellServer){
+			return;
+		}
+		this.log("sending subscription to: "+topic);
+		// create a subscription event object and give it all the props we need
+		// to updates on the specified topic
+
+		// FIXME: we should only enqueue if this is our first subscription!
+		this.sendTopicSubToServer(topic, rstr);
+	}
+
+	this.sendTopicSubToServer = function(topic, str){
+		if(!this.attachPathList[topic]["subscriptions"]){
+			this.enqueueEventStr(str);
+			this.attachPathList[topic].subscriptions = 0;
+		}
+		this.attachPathList[topic].subscriptions++;
+	}
+
+	this.unSubscribe = function(topic, toObj, toFunc){
+		// first, locally disconnect
+		dojo.event.kwDisconnect({
+			srcObj: this.attachPathList, 
+			srcFunc: topic, 
+			adviceObj: toObj, 
+			adviceFunc: toFunc
+		});
+		
+		// FIXME: figure out if there are any remaining listeners to the topic,
+		// 		  and if not, inform the server of our desire not to be
+		// 		  notified of updates to the topic
+	}
+
+	// the "publish" method is really a misnomer, since it really means "take
+	// this event and send it to the server". Note that the "dispatch" method
+	// handles local event promigulation, and therefore we emulate both sides
+	// of a real event router without having to swallow all of the complexity.
+	this.publish = function(topic, event){
+		var evt = dojo.io.repubsubEvent.initFromProperties(event);
+		// FIXME: need to make sure we have from and to set correctly
+		// 		  before we serialize and send off to the great blue
+		// 		  younder.
+		evt.to = topic;
+		// evt.from = this.tunnelURI;
+
+		var evtURLParts = [];
+		evtURLParts.push(this.serverBaseURL+"/kn");
+
+		// serialize the event to a string and then post it to the correct
+		// topic
+		evtURLParts.push(evt.toGetString());
+		this.enqueueEventStr(evtURLParts.join(""));
+	}
+
+	this.enqueueEventStr = function(evtStr){
+		this.log("enqueueEventStr");
+		this.backlog.push(evtStr);
+		this.dequeueEvent();
+	}
+
+	this.dequeueEvent = function(force){
+		this.log("dequeueEvent");
+		if(this.backlog.length <= 0){ return; }
+		if((this.canSnd)||(force)){
+			dojo.io.setIFrameSrc(this.sndNode, this.backlog.shift()+"&callback=repubsub.sndNodeReady");
+			this.canSnd = false;
+		}else{
+			this.log("sndNode not available yet!", "debug");
+		}
+	}
+}
+
+dojo.io.repubsubEvent = function(to, from, method, id, routeURI, payload, dispname, uid){
+	this.to = to;
+	this.from = from;
+	this.method = method||"route";
+	this.id = id||repubsub.getRandStr();
+	this.uri = routeURI;
+	this.displayname = dispname||repubsub.displayname;
+	this.userid = uid||repubsub.userid;
+	this.payload = payload||"";
+	this.flushChars = 4096;
+
+	this.initFromProperties = function(evt){
+		if(evt.constructor = dojo.io.repubsubEvent){ 
+			for(var x in evt){
+				this[x] = evt[x];
+			}
+		}else{
+			// we want to copy all the properties of the evt object, and transform
+			// those that are "stock" properties of dojo.io.repubsubEvent. All others should
+			// be copied as-is
+			for(var x in evt){
+				if(typeof this.forwardPropertiesMap[x] == "string"){
+					this[this.forwardPropertiesMap[x]] = evt[x];
+				}else{
+					this[x] = evt[x];
+				}
+			}
+		}
+	}
+
+	this.toGetString = function(noQmark){
+		var qs = [ ((noQmark) ? "" : "?") ];
+		for(var x=0; x<this.properties.length; x++){
+			var tp = this.properties[x];
+			if(this[tp[0]]){
+				qs.push(tp[1]+"="+encodeURIComponent(String(this[tp[0]])));
+			}
+			// FIXME: we need to be able to serialize non-stock properties!!!
+		}
+		return qs.join("&");
+	}
+
+}
+
+dojo.io.repubsubEvent.prototype.properties = [["from", "kn_from"], ["to", "kn_to"], 
+									["method", "do_method"], ["id", "kn_id"], 
+									["uri", "kn_uri"], 
+									["displayname", "kn_displayname"], 
+									["userid", "kn_userid"], 
+									["payload", "kn_payload"],
+									["flushChars", "kn_response_flush"],
+									["responseFormat", "kn_response_format"] ];
+
+// maps properties from their old names to their new names...
+dojo.io.repubsubEvent.prototype.forwardPropertiesMap = {};
+// ...and vice versa...
+dojo.io.repubsubEvent.prototype.reversePropertiesMap = {};
+
+// and we then populate them both from the properties list
+for(var x=0; x<dojo.io.repubsubEvent.prototype.properties.length; x++){
+	var tp = dojo.io.repubsubEvent.prototype.properties[x];
+	dojo.io.repubsubEvent.prototype.reversePropertiesMap[tp[0]] = tp[1];
+	dojo.io.repubsubEvent.prototype.forwardPropertiesMap[tp[1]] = tp[0];
+}
+// static version of initFromProperties, creates new event and object and
+// returns it after init
+dojo.io.repubsubEvent.initFromProperties = function(evt){
+	var eventObj = new dojo.io.repubsubEvent();
+	eventObj.initFromProperties(evt);
+	return eventObj;
+}

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RepubsubIO.js
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RhinoIO.js
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RhinoIO.js?view=auto&rev=451106
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RhinoIO.js (added)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RhinoIO.js Thu Sep 28 20:42:39 2006
@@ -0,0 +1,147 @@
+/*
+	Copyright (c) 2004-2006, The Dojo Foundation
+	All Rights Reserved.
+
+	Licensed under the Academic Free License version 2.1 or above OR the
+	modified BSD license. For more information on Dojo licensing, see:
+
+		http://dojotoolkit.org/community/licensing.shtml
+*/
+
+dojo.provide("dojo.io.RhinoIO");
+
+dojo.require("dojo.io.common");
+dojo.require("dojo.lang.func");
+dojo.require("dojo.lang.array");
+dojo.require("dojo.string.extras");
+
+dojo.io.RhinoHTTPTransport = new function(){
+		this.canHandle = function(req){
+			// We have to limit to text types because Rhino doesnt support 
+			// a W3C dom implementation out of the box.  In the future we 
+			// should provide some kind of hook to inject your own, because
+			// in all my projects I use XML for Script to provide a W3C DOM.
+			if(!dojo.lang.inArray((req.mimetype.toLowerCase() || ""),
+				["text/plain", "text/html", "text/javascript", "text/json"])){
+				return false;
+			}
+			
+			// We only handle http requests!  Unfortunately, because the method is 
+			// protected, I can't directly create a java.net.HttpURLConnection, so
+			// this is the only way to test.
+			if(req.url.substr(0, 7) != "http://"){
+				return false;
+			}
+			
+			return true;
+		}
+
+		function doLoad(req, conn){
+			var ret;
+			if (req.method.toLowerCase() == "head"){
+				// TODO: return the headers
+			}else{
+				var stream = conn.getContent();
+				var reader = new java.io.BufferedReader(new java.io.InputStreamReader(stream));
+
+				// read line-by-line because why not?
+				var text = "";
+				var line = null;
+				while((line = reader.readLine()) != null){
+					text += line;
+				}
+
+				if(req.mimetype == "text/javascript"){
+					try{
+						ret = dj_eval(text);
+					}catch(e){
+						dojo.debug(e);
+						dojo.debug(text);
+						ret = null;
+					}
+				}else if(req.mimetype == "text/json"){
+					try{
+						ret = dj_eval("("+text+")");
+					}catch(e){
+						dojo.debug(e);
+						dojo.debug(text);
+						ret = false;
+					}
+				}else{
+					ret = text;
+				}
+			}
+
+			req.load("load", ret, req);
+		}
+		
+		function connect(req){
+			var content = req.content || {};
+			var query;
+	
+			if (req.sendTransport){
+				content["dojo.transport"] = "rhinohttp";
+			}
+	
+			if(req.postContent){
+				query = req.postContent;
+			}else{
+				query = dojo.io.argsFromMap(content, req.encoding);
+			}
+	
+			var url_text = req.url;
+			if(req.method.toLowerCase() == "get" && query != ""){
+				url_text = url_text + "?" + query;
+			}
+			
+			var url  = new java.net.URL(url_text);
+			var conn = url.openConnection();
+			
+			//
+			// configure the connection
+			//
+			
+			conn.setRequestMethod(req.method.toUpperCase());
+			
+			if(req.headers){
+				for(var header in req.headers){
+					if(header.toLowerCase() == "content-type" && !req.contentType){
+						req.contentType = req.headers[header];
+					}else{
+						conn.setRequestProperty(header, req.headers[header]);
+					}
+				}
+			}
+			if(req.contentType){
+				conn.setRequestProperty("Content-Type", req.contentType);
+			}
+
+			if(req.method.toLowerCase() == "post"){
+				conn.setDoOutput(true);
+
+				// write the post data
+				var output_stream = conn.getOutputStream();
+				var byte_array = (new java.lang.String(query)).getBytes();
+				output_stream.write(byte_array, 0, byte_array.length);
+			}
+			
+			// do it to it!
+			conn.connect();
+
+			// perform the load
+			doLoad(req, conn);
+		}
+		
+		this.bind = function(req){
+			var async = req["sync"] ? false : true;
+			if (async){
+				setTimeout(dojo.lang.hitch(this, function(){
+					connect(req);
+				}), 1);
+			} else {
+				connect(req);
+			}
+		}
+
+		dojo.io.transports.addTransport("RhinoHTTPTransport");
+}

Propchange: tapestry/tapestry4/trunk/tapestry-framework/src/js/dojo/src/io/RhinoIO.js
------------------------------------------------------------------------------
    svn:eol-style = native