You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by jk...@apache.org on 2006/05/13 18:37:00 UTC
svn commit: r406128 [11/27] - in
/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo:
./ src/ src/animation/ src/collections/ src/compat/ src/crypto/ src/data/
src/data/format/ src/data/provider/ src/debug/ src/dnd/ src/event/ sr...
Added: tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/iCalendar.js
URL: http://svn.apache.org/viewcvs/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/iCalendar.js?rev=406128&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/iCalendar.js (added)
+++ tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/iCalendar.js Sat May 13 09:36:25 2006
@@ -0,0 +1,814 @@
+/*
+ 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.text.textDirectory");
+dojo.require("dojo.date");
+dojo.require("dojo.lang");
+
+
+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.lang.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];
+ 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.lang.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.lang.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 {
+ startDate = prop.getDate();
+ month = startDate.getMonth() + 1;
+ 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++) {
+ month = dates[y].getMonth() + 1;
+ 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 = [];
+ 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.lang.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) {
+ tmpUntil = dojo.date.fromIso8601(rrule.until);
+ } else {
+ 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":
+ 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];
+ 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]);
+ 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);
+ 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]);
+ 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);
+
Added: tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io.js
URL: http://svn.apache.org/viewcvs/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io.js?rev=406128&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io.js (added)
+++ tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io.js Sat May 13 09:36:25 2006
@@ -0,0 +1,366 @@
+/*
+ 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.IO");
+dojo.require("dojo.string");
+dojo.require("dojo.lang.extras");
+
+/******************************************************************************
+ * Notes about dojo.io design:
+ *
+ * The dojo.io.* package has the unenviable task of making a lot of different
+ * types of I/O feel natural, despite a universal lack of good (or even
+ * reasonable!) I/O capability in the host environment. So lets pin this down
+ * a little bit further.
+ *
+ * Rhino:
+ * perhaps the best situation anywhere. Access to Java classes allows you
+ * to do anything one might want in terms of I/O, both synchronously and
+ * async. Can open TCP sockets and perform low-latency client/server
+ * interactions. HTTP transport is available through Java HTTP client and
+ * server classes. Wish it were always this easy.
+ *
+ * xpcshell:
+ * XPCOM for I/O. A cluster-fuck to be sure.
+ *
+ * spidermonkey:
+ * S.O.L.
+ *
+ * Browsers:
+ * Browsers generally do not provide any useable filesystem access. We are
+ * therefore limited to HTTP for moving information to and from Dojo
+ * instances living in a browser.
+ *
+ * XMLHTTP:
+ * Sync or async, allows reading of arbitrary text files (including
+ * JS, which can then be eval()'d), writing requires server
+ * cooperation and is limited to HTTP mechanisms (POST and GET).
+ *
+ * <iframe> hacks:
+ * iframe document hacks allow browsers to communicate asynchronously
+ * with a server via HTTP POST and GET operations. With significant
+ * effort and server cooperation, low-latency data transit between
+ * client and server can be acheived via iframe mechanisms (repubsub).
+ *
+ * SVG:
+ * Adobe's SVG viewer implements helpful primitives for XML-based
+ * requests, but receipt of arbitrary text data seems unlikely w/o
+ * <![CDATA[]]> sections.
+ *
+ *
+ * A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
+ * the IO API interface. A transcript of it can be found at:
+ * http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
+ *
+ * Also referenced in the design of the API was the DOM 3 L&S spec:
+ * http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
+ ******************************************************************************/
+
+// a map of the available transport options. Transports should add themselves
+// by calling add(name)
+dojo.io.transports = [];
+dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
+
+dojo.io.Request = function(url, mimetype, transport, changeUrl){
+ if((arguments.length == 1)&&(arguments[0].constructor == Object)){
+ this.fromKwArgs(arguments[0]);
+ }else{
+ this.url = url;
+ if(mimetype){ this.mimetype = mimetype; }
+ if(transport){ this.transport = transport; }
+ if(arguments.length >= 4){ this.changeUrl = changeUrl; }
+ }
+}
+
+dojo.lang.extend(dojo.io.Request, {
+
+ /** The URL to hit */
+ url: "",
+
+ /** The mime type used to interrpret the response body */
+ mimetype: "text/plain",
+
+ /** The HTTP method to use */
+ method: "GET",
+
+ /** An Object containing key-value pairs to be included with the request */
+ content: undefined, // Object
+
+ /** The transport medium to use */
+ transport: undefined, // String
+
+ /** If defined the URL of the page is physically changed */
+ changeUrl: undefined, // String
+
+ /** A form node to use in the request */
+ formNode: undefined, // HTMLFormElement
+
+ /** Whether the request should be made synchronously */
+ sync: false,
+
+ bindSuccess: false,
+
+ /** Cache/look for the request in the cache before attempting to request?
+ * NOTE: this isn't a browser cache, this is internal and would only cache in-page
+ */
+ useCache: false,
+
+ /** Prevent the browser from caching this by adding a query string argument to the URL */
+ preventCache: false,
+
+ // events stuff
+ load: function(type, data, evt){ },
+ error: function(type, error){ },
+ timeout: function(type){ },
+ handle: function(){ },
+
+ //FIXME: change BrowserIO.js to use timeouts? IframeIO?
+ // The number of seconds to wait until firing a timeout callback.
+ // If it is zero, that means, don't do a timeout check.
+ timeoutSeconds: 0,
+
+ // the abort method needs to be filled in by the transport that accepts the
+ // bind() request
+ abort: function(){ },
+
+ // backButton: function(){ },
+ // forwardButton: function(){ },
+
+ fromKwArgs: function(kwArgs){
+ // normalize args
+ if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
+ if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
+ if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
+ kwArgs.method = kwArgs["formNode"].method;
+ }
+
+ // backwards compatibility
+ if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
+ if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
+ if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
+
+ // encoding fun!
+ kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
+
+ kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
+
+ var isFunction = dojo.lang.isFunction;
+ for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+ var fn = dojo.io.hdlrFuncNames[x];
+ if(isFunction(kwArgs[fn])){ continue; }
+ if(isFunction(kwArgs["handle"])){
+ kwArgs[fn] = kwArgs.handle;
+ }
+ // handler is aliased above, shouldn't need this check
+ /* else if(dojo.lang.isObject(kwArgs.handler)){
+ if(isFunction(kwArgs.handler[fn])){
+ kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
+ }
+ }*/
+ }
+ dojo.lang.mixin(this, kwArgs);
+ }
+
+});
+
+dojo.io.Error = function(msg, type, num){
+ this.message = msg;
+ this.type = type || "unknown"; // must be one of "io", "parse", "unknown"
+ this.number = num || 0; // per-substrate error number, not normalized
+}
+
+dojo.io.transports.addTransport = function(name){
+ this.push(name);
+ // FIXME: do we need to handle things that aren't direct children of the
+ // dojo.io namespace? (say, dojo.io.foo.fooTransport?)
+ this[name] = dojo.io[name];
+}
+
+// binding interface, the various implementations register their capabilities
+// and the bind() method dispatches
+dojo.io.bind = function(request){
+ // if the request asks for a particular implementation, use it
+ if(!(request instanceof dojo.io.Request)){
+ try{
+ request = new dojo.io.Request(request);
+ }catch(e){ dojo.debug(e); }
+ }
+ var tsName = "";
+ if(request["transport"]){
+ tsName = request["transport"];
+ // FIXME: it would be good to call the error handler, although we'd
+ // need to use setTimeout or similar to accomplish this and we can't
+ // garuntee that this facility is available.
+ if(!this[tsName]){ return request; }
+ }else{
+ // otherwise we do our best to auto-detect what available transports
+ // will handle
+ for(var x=0; x<dojo.io.transports.length; x++){
+ var tmp = dojo.io.transports[x];
+ if((this[tmp])&&(this[tmp].canHandle(request))){
+ tsName = tmp;
+ }
+ }
+ if(tsName == ""){ return request; }
+ }
+ this[tsName].bind(request);
+ request.bindSuccess = true;
+ return request;
+}
+
+dojo.io.queueBind = function(request){
+ if(!(request instanceof dojo.io.Request)){
+ try{
+ request = new dojo.io.Request(request);
+ }catch(e){ dojo.debug(e); }
+ }
+
+ // make sure we get called if/when we get a response
+ var oldLoad = request.load;
+ request.load = function(){
+ dojo.io._queueBindInFlight = false;
+ var ret = oldLoad.apply(this, arguments);
+ dojo.io._dispatchNextQueueBind();
+ return ret;
+ }
+
+ var oldErr = request.error;
+ request.error = function(){
+ dojo.io._queueBindInFlight = false;
+ var ret = oldErr.apply(this, arguments);
+ dojo.io._dispatchNextQueueBind();
+ return ret;
+ }
+
+ dojo.io._bindQueue.push(request);
+ dojo.io._dispatchNextQueueBind();
+ return request;
+}
+
+dojo.io._dispatchNextQueueBind = function(){
+ if(!dojo.io._queueBindInFlight){
+ dojo.io._queueBindInFlight = true;
+ if(dojo.io._bindQueue.length > 0){
+ dojo.io.bind(dojo.io._bindQueue.shift());
+ }else{
+ dojo.io._queueBindInFlight = false;
+ }
+ }
+}
+dojo.io._bindQueue = [];
+dojo.io._queueBindInFlight = false;
+
+dojo.io.argsFromMap = function(map, encoding, last){
+ var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
+ var mapped = [];
+ var control = new Object();
+ for(var name in map){
+ var domap = function(elt){
+ var val = enc(name)+"="+enc(elt);
+ mapped[(last == name) ? "push" : "unshift"](val);
+ }
+ if(!control[name]){
+ var value = map[name];
+ // FIXME: should be isArrayLike?
+ if (dojo.lang.isArray(value)){
+ dojo.lang.forEach(value, domap);
+ }else{
+ domap(value);
+ }
+ }
+ }
+ return mapped.join("&");
+}
+
+dojo.io.setIFrameSrc = function(iframe, src, replace){
+ try{
+ var r = dojo.render.html;
+ // dojo.debug(iframe);
+ if(!replace){
+ if(r.safari){
+ iframe.location = src;
+ }else{
+ frames[iframe.name].location = src;
+ }
+ }else{
+ // Fun with DOM 0 incompatibilities!
+ var idoc;
+ if(r.ie){
+ idoc = iframe.contentWindow.document;
+ }else if(r.safari){
+ idoc = iframe.document;
+ }else{ // if(r.moz){
+ idoc = iframe.contentWindow;
+ }
+ idoc.location.replace(src);
+ }
+ }catch(e){
+ dojo.debug(e);
+ dojo.debug("setIFrameSrc: "+e);
+ }
+}
+
+/*
+dojo.io.sampleTranport = new function(){
+ this.canHandle = function(kwArgs){
+ // canHandle just tells dojo.io.bind() if this is a good transport to
+ // use for the particular type of request.
+ if(
+ (
+ (kwArgs["mimetype"] == "text/plain") ||
+ (kwArgs["mimetype"] == "text/html") ||
+ (kwArgs["mimetype"] == "text/javascript")
+ )&&(
+ (kwArgs["method"] == "get") ||
+ ( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
+ )
+ ){
+ return true;
+ }
+
+ return false;
+ }
+
+ this.bind = function(kwArgs){
+ var hdlrObj = {};
+
+ // set up a handler object
+ for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
+ var fn = dojo.io.hdlrFuncNames[x];
+ if(typeof kwArgs.handler == "object"){
+ if(typeof kwArgs.handler[fn] == "function"){
+ hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
+ }
+ }else if(typeof kwArgs[fn] == "function"){
+ hdlrObj[fn] = kwArgs[fn];
+ }else{
+ hdlrObj[fn] = kwArgs["handle"]||function(){};
+ }
+ }
+
+ // build a handler function that calls back to the handler obj
+ var hdlrFunc = function(evt){
+ if(evt.type == "onload"){
+ hdlrObj.load("load", evt.data, evt);
+ }else if(evt.type == "onerr"){
+ var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
+ hdlrObj.error("error", errObj);
+ }
+ }
+
+ // the sample transport would attach the hdlrFunc() when sending the
+ // request down the pipe at this point
+ var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
+ // sampleTransport.sendRequest(tgtURL, hdlrFunc);
+ }
+
+ dojo.io.transports.addTransport("sampleTranport");
+}
+*/
Added: tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/BrowserIO.js
URL: http://svn.apache.org/viewcvs/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/BrowserIO.js?rev=406128&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/BrowserIO.js (added)
+++ tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/BrowserIO.js Sat May 13 09:36:25 2006
@@ -0,0 +1,556 @@
+/*
+ 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");
+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(type, ["file", "submit", "image", "reset", "button"]);
+}
+
+// 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(type, ["radio", "checkbox"])){
+ 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(node.type.toLowerCase(), ["submit", "button"])) {
+ 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(type, ["submit", "button", "image"])) {
+ if(!this.clickedButton) { this.clickedButton = node; }
+ accept = node == this.clickedButton;
+ } else {
+ accept = !dojo.lang.inArray(type, ["file", "submit", "reset", "button"]);
+ }
+ 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==304)||(http.status==204)||
+ (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") {
+ 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){
+ this.inFlightTimer = setInterval("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
+ }
+ }
+
+ this.watchInFlight = function(){
+ var now = null;
+ for(var x=this.inFlight.length-1; x>=0; x--){
+ var tif = this.inFlight[x];
+ if(!tif){ 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);
+ }
+ }
+ }
+
+ if(this.inFlight.length == 0){
+ clearInterval(this.inFlightTimer);
+ this.inFlightTimer = null;
+ }
+ }
+
+ 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((kwArgs["mimetype"].toLowerCase()||""), ["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json"])
+ && !( 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();
+ }
+
+ if(kwArgs.method.toLowerCase() == "post"){
+ // FIXME: need to hack in more flexible Content-Type setting here!
+ http.open("POST", url, async);
+ 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);
+ }
+
+ kwArgs.abort = function(){
+ return http.abort();
+ }
+
+ return;
+ }
+ dojo.io.transports.addTransport("XMLHTTPTransport");
+}
Added: tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/IframeIO.js
URL: http://svn.apache.org/viewcvs/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/IframeIO.js?rev=406128&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/IframeIO.js (added)
+++ tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/IframeIO.js Sat May 13 09:36:25 2006
@@ -0,0 +1,241 @@
+/*
+ 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){
+ if(window[fname]){ return window[fname]; }
+ if(window.frames[fname]){ return window.frames[fname]; }
+ var r = dojo.render.html;
+ var cframe = null;
+ var turi = 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;
+ }
+ (document.body||document.getElementsByTagName("body")[0]).appendChild(cframe);
+ window[fname] = cframe;
+ with(cframe.style){
+ 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;
+}
+
+// thanks burstlib!
+dojo.io.iframeContentWindow = function(iframe_el) {
+ var win = iframe_el.contentWindow || // IE
+ dojo.io.iframeContentDocument(iframe_el).defaultView || // Moz, opera
+ // Moz. TODO: is this available when defaultView isn't?
+ dojo.io.iframeContentDocument(iframe_el).__parent__ ||
+ (iframe_el.name && document.frames[iframe_el.name]) || null;
+ return win;
+}
+
+dojo.io.iframeContentDocument = 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;
+}
+
+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(kwArgs["mimetype"],
+ [ "text/plain", "text/html",
+ "application/xml", "text/xml",
+ "text/javascript", "text/json"])
+ )&&(
+ // make sur we really only get used in file upload cases
+ (kwArgs["formNode"])&&(dojo.io.checkChildrenForFile(kwArgs["formNode"]))
+ )&&(
+ dojo.lang.inArray(kwArgs["method"].toLowerCase(), ["post", "get"])
+ )&&(
+ // 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];
+ 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 ifr = _this.iframe;
+ var ifw = dojo.io.iframeContentWindow(ifr);
+ // 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 cd = dojo.io.iframeContentDocument(_this.iframe);
+ var js = cd.getElementsByTagName("textarea")[0].value;
+ if(cmt == "text/json") { js = "(" + js + ")"; }
+ value = dj_eval(js);
+ }else if((cmt == "application/xml")||(cmt == "text/xml")){
+ value = dojo.io.iframeContentDocument(_this.iframe);
+ }else{ // text/plain
+ value = ifw.innerHTML;
+ }
+ 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");
+}
Added: tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RepubsubIO.js
URL: http://svn.apache.org/viewcvs/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RepubsubIO.js?rev=406128&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RepubsubIO.js (added)
+++ tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RepubsubIO.js Sat May 13 09:36:25 2006
@@ -0,0 +1,517 @@
+// Copyright (c) 2004 Friendster Inc., Licensed under the Academic Free
+// License version 2.0 or later
+
+dojo.require("dojo.event.Event");
+dojo.require("dojo.event.BrowserEvent");
+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 = function(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;
+}
Added: tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RhinoIO.js
URL: http://svn.apache.org/viewcvs/tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RhinoIO.js?rev=406128&view=auto
==============================================================================
--- tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RhinoIO.js (added)
+++ tapestry/tapestry4/trunk/framework/src/java/org/apache/tapestry/html/dojo/src/io/RhinoIO.js Sat May 13 09:36:25 2006
@@ -0,0 +1,22 @@
+/*
+ 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");
+
+// TODO: this doesn't execute
+/*dojo.io.SyncHTTPRequest = function(){
+ dojo.io.SyncRequest.call(this);
+
+ this.send = function(URI){
+ }
+}
+
+dojo.inherits(dojo.io.SyncHTTPRequest, dojo.io.SyncRequest);
+*/