Update of /cvsroot/dynapi/dynapi/src/lib/dynapi/event
In directory usw-pr-cvs1:/tmp/cvs-serv30523/src/lib/dynapi/event
Added Files:
dragevent.js keyboard.js listeners.js mouse.js simple.js
Log Message:
Imported DynAPIX
--- NEW FILE ---
/*
DynAPI Distribution
DragEvent Class
The DynAPI Distribution is distributed under the terms of the GNU LGPL license.
*/
// DragEvent object
function DragEvent(type,src) {
this.DynEvent = DynEvent
this.DynEvent()
this.dragEnabled=true;
}
DragEvent.prototype = new DynEvent()
DragEvent.prototype.getX=function() {return this.x;};
DragEvent.prototype.getY=function() {return this.y;};
DragEvent.prototype.getPageX=function() {return this.pageX;};
DragEvent.prototype.getPageY=function() {return this.pageY;};
DragEvent.prototype.cancelDrag=function() {this.dragEnabled=false;};
DragEvent.dragPlay=0;
DragEvent.dragevent=new DragEvent();
DragEvent.lyrListener=new EventListener();
DragEvent.lyrListener.onmousedown=function(e) {
e.cancelBrowserEvent();
if (DragEvent.dragevent.src) return;
var lyr=e.getSource();
if (is.ie) lyr.doc.body.onselectstart = function() { return false; }
// Initialize dragEvent object
var de=DragEvent.dragevent;
de.type="dragstart"
de.src=lyr
// Set properties
de.isDragging=false;
de.x=e.getPageX()-e.getSource().getPageX();
de.y=e.getPageY()-e.getSource().getPageY();
de.pageX=e.getPageX();
de.pageY=e.getPageY();
de.parentPageX=lyr.parent.getPageX();
de.parentPageY=lyr.parent.getPageY();
};
DragEvent.docListener=new EventListener();
DragEvent.docListener.onmousemove=function(e) {
// Get, if any, the currently drag in process and the layer. If none, return
var de = DragEvent.dragevent;
if (!de) return;
var lyr = de.src;
if (!lyr) return;
if(!de.isDragging)
// Detect if we should start the drag
if(DragEvent.dragPlay==0 ||
(Math.abs(de.pageX-e.getPageX())-DragEvent.dragPlay>0) ||
(Math.abs(de.pageY-e.getPageY())-DragEvent.dragPlay>0)) {
de.isDragging=true;
de.src.invokeEvent("dragstart",de);
e.setBubble(de.bubble);
}
if (!de.isDragging) return;
else if (!de.dragEnabled) {
// This allows 'cancelDrag' method to fire the mouseUp as if had been
released by the user
lyr.invokeEvent("mouseup");
return;
}
// Properties
de.type="dragmove";
de.pageX=e.getPageX();
de.pageY=e.getPageY();
var x=de.pageX-de.parentPageX-de.x;
var y=de.pageY-de.parentPageY-de.y;
// Respect boundary, if any
if (lyr.dragBoundary) {
var dB=lyr.dragBoundary;
if (dB=="parent") {
var b=lyr.parent.getHeight();
var r=lyr.parent.getWidth();
var l=0;
var t=0;
} else {
var b=dB[2];
var r=dB[1];
var l=dB[3];
var t=dB[0];
}
var w=lyr.w;
var h=lyr.h;
if (x<l) x=l;
else if (x+w>r) x=r-w;
if (y<t) y=t;
else if (y+h>b) y=b-h;
}
// Move dragged layer
lyr.moveTo(x,y);
lyr.invokeEvent("dragmove",de);
e.cancelBrowserEvent();
e.setBubble(de.bubble);
};
DragEvent.docListener.onmouseup=function(e) {
// Get, if any, the currently drag in process and the layer. If none, return
var de=DragEvent.dragevent;
if (!de) return;
var lyr=de.src;
if (!lyr) return;
if (!de.isDragging) {
de.type="dragend";
de.src=null;
e.setBubble(true);
return;
}
if (is.ie) lyr.doc.body.onselectstart = null;
// Avoid click for the dragged layer ( with MouseEvent addition )
if (is.def) DynAPI.wasDragging=true;
// Properties for the event
de.type="dragend";
de.isDragging=false;
lyr.invokeEvent("dragend",de);
// Clean drag stuff
de.src=null;
e.cancelBrowserEvent();
e.setBubble(de.bubble);
};
DragEvent.setDragBoundary=function(dlyr,t,r,b,l) {
var a=arguments;
if (a.length==0) return;
if (a.length==1) dlyr.dragBoundary="parent";
else if (a.length==5) dlyr.dragBoundary=new Array(t,r,b,l);
};
DragEvent.enableDragEvents=function(f) {
for (var i=0;i<arguments.length;i++) {
var lyr=arguments[i];
if (lyr.isDynLayer) lyr.addEventListener(DragEvent.lyrListener);
}
if(f.isDynDocument) f.addEventListener(DragEvent.docListener);
else DynAPI.document.addEventListener(DragEvent.docListener);
};
DragEvent.disableDragEvents=function() {
for (var i=0;i<arguments.length;i++) {
var lyr=arguments[i];
lyr.removeEventListener(DragEvent.lyrListener);
}
};
--- NEW FILE ---
/*
DynAPI Distribution
Key Event Extensions by Henrik V�glin ([EMAIL PROTECTED])
The DynAPI Distribution is distributed under the terms of the GNU LGPL license.
Requirements:
dynapi.api [dynlayer, dyndocument, browser]
dynapi.event [listeners]
*/
DynKeyEvent=function() {
this.DynEvent=DynEvent;
this.DynEvent();
};
DynKeyEvent.prototype=new DynEvent();
DynKeyEvent.prototype.getKey=function() {
return this.charKey
};
DynKeyEvent.EventMethod = function(e) {
var dynobject=this.lyrobj;
if(is.def) {
// We won't receive the event in IE. We have to go and get it from the
frame
if (is.ie) var e=dynobject.frame.event;
// Netscape 6 and alikes
else if (e.eventPhase!=3) return;
// Both NS6/Moz and IE
e.cancelBubble=true;
}
if(is.def) var realsrc =
Methods.getContainerLayerOf(is.ie?e.srcElement:e.target) || dynobject
else if(is.ns4) var realsrc=e.target.lyrobj
// Init event object
var type = e.type
var evt=DynKeyEvent._e
// Set properties
evt.src=realsrc;
evt.which=(is.ns4)?e.which:e.keyCode;
var curKey = String.fromCharCode(evt.which).toLowerCase();
if (((curKey>='a')&&(curKey<='z'))||((curKey>='0')&&(curKey<='9')))
evt.charKey=curKey;
evt.ctrlKey=(is.ns4)?(e.modifiers &
Event.CONTROL_MASK):(e.ctrlKey||e.ctrlLeft||e.keyCode==17);
evt.shiftKey=(is.ns4)?(e.modifiers &
Event.SHIFT_MASK):(e.shiftKey||e.shiftLeft||e.keyCode==16);
// Pointer to original event object
evt.orig=e;
// Invoke event
realsrc.invokeEvent(type,evt);
// Return to browser
return e.browserReturn
};
DynKeyEvent._e=new DynKeyEvent();
DynDocument.prototype.captureKeyEvents=function() {
if(is.def&&!is.ie) {
this.doc.addEventListener("keydown",DynKeyEvent.EventMethod,false)
this.doc.addEventListener("keyup",DynKeyEvent.EventMethod,false)
this.doc.addEventListener("keypress",DynKeyEvent.EventMethod,false)
}
else {
if (is.ns4) this.doc.captureEvents(Event.KEYPRESS | Event.KEYDOWN |
Event.KEYUP);
this.doc.onkeypress=this.doc.onkeydown=this.doc.onkeyup=DynKeyEvent.EventMethod
}
}
DynDocument.prototype.releaseKeyEvents=function() {
if(is.def&&!is.ie) {
this.doc.removeEventListener("keydown",DynKeyEvent.EventMethod,false)
this.doc.removeEventListener("keyup",DynKeyEvent.EventMethod,false)
this.doc.removeEventListener("keypress",DynKeyEvent.EventMethod,false)
}
else {
if (is.ns4) this.doc.releaseEvents(Event.KEYPRESS | Event.KEYDOWN |
Event.KEYUP);
this.doc.onkeypress=this.doc.onkeydown=this.doc.onkeyup=null
}
}
DynLayer.prototype.captureKeyEvents=function() {
var elm = this.elm
if(!elm) return
if(is.def&&!is.ie) {
this.elm.addEventListener("keydown",DynKeyEvent.EventMethod,false)
this.elm.addEventListener("keyup",DynKeyEvent.EventMethod,false)
this.elm.addEventListener("keypress",DynKeyEvent.EventMethod,false)
}
else {
if (is.ns4) this.elm.captureEvents(Event.KEYPRESS | Event.KEYDOWN |
Event.KEYUP);
this.elm.onkeypress=this.elm.onkeydown=this.elm.onkeyup=DynKeyEvent.EventMethod
}
}
DynLayer.prototype.releaseKeyEvents=function() {
var elm = this.elm
if(!elm) return
if(is.def&&!is.ie) {
this.elm.removeEventListener("keydown",DynKeyEvent.EventMethod,false)
this.elm.removeEventListener("keyup",DynKeyEvent.EventMethod,false)
this.elm.removeEventListener("keypress",DynKeyEvent.EventMethod,false)
}
else {
if (is.ns4) this.elm.releaseEvents(Event.KEYPRESS | Event.KEYDOWN |
Event.KEYUP);
this.elm.onkeypress=this.elm.onkeydown=this.elm.onkeyup=null
}
}
/* Overwrite methods to support key events. No more addCodeToMethod */
// Common
DynObject.prototype.assignKeyEvents = function() {
if (this.hasEventListeners) this.captureKeyEvents()
var l=this.children.length;
for (var i=0; i<l; i++) this.children[i].assignKeyEvents()
}
DynObject.prototype._OldK_addEventListener = DynObject.prototype.addEventListener
DynObject.prototype.addEventListener = function(l) {
var r = this._OldK_addEventListener(l)
if(this.hasEventListeners && this.created) this.captureKeyEvents()
return r
}
DynObject.prototype._OldK_removeEventListener = DynObject.prototype.removeEventListener
DynObject.prototype.removeEventListener = function(l) {
var r = this._OldK_removeEventListener(l)
if(!this.hasEventListeners) this.releaseKeyEvents()
return r
}
DynObject.prototype._OldK_removeAllEventListeners =
DynObject.prototype.removeAllEventListeners
DynObject.prototype.removeAllEventListeners = function() {
var r = this._OldK_removeAllEventListeners()
this.releaseKeyEvents()
return r
}
// DynLayer Specific
DynLayer.prototype._OldK_specificCreate = DynLayer.prototype.specificCreate
DynLayer.prototype.specificCreate = function() {
this._OldK_specificCreate()
this.assignKeyEvents()
}
// DynDocument specific
DynDocument.prototype._OldK_specificCreate = DynDocument.prototype.specificCreate
DynDocument.prototype.specificCreate = function() {
this._OldK_specificCreate()
this.assignKeyEvents()
}
// CH: removeAllEventListeners now happens in del() defined in listeners.js.
--- NEW FILE ---
/*
DynAPI Distribution
Advanced Event object. Generic Event Listeners
The DynAPI Distribution is distributed under the terms of the GNU LGPL license.
*/
DynEvent=function(type,src,target) {
this.type=type;
this.src=src;
this.target=target;
this.bubble=false;
};
DynEvent.prototype.setBubble=function(b) {this.bubble=b};
DynEvent.prototype.getType=function() {return this.type};
DynEvent.prototype.getSource=function() {return this.src};
DynEvent.prototype.getTarget=function() {return this.target};
DynEvent.prototype.preBubbleCode = function() {};
EventListener = function(target) { this.target=target };
EventListener.prototype.handleEvent=function(type,e,args) {
if(this["on"+type]) this["on"+type](e,args);
};
// Extend DynObject to support EventListeners
DynObject.prototype.addEventListener=function(listener) {
if(!this.eventListeners) { this.eventListeners = [];
this.setHigherListener(this.higherListener); }
this.hasEventListeners = true;
for (var i in this.eventListeners) if (this.eventListeners[i]==listener)
return;
this.eventListeners[this.eventListeners.length]=listener;
}
DynObject.prototype.removeEventListener=function(listener) {
Methods.removeFromArray(this.eventListeners, listener, false);
if(this.eventListeners.length==0) {
this.hasEventListeners=false;
this.setHigherListener(this.higherListener)
}
}
DynObject.prototype.removeAllEventListeners=function() {
if (!this.hasEventListeners) return;
for (var i in this.eventListeners) delete this.eventListeners[i];
this.eventListeners=[];
this.hasEventListeners=false;
this.setHigherListener(this.higherListener)
}
DynObject.prototype.invokeEvent=function(type,e,args) {
if (!e) e=new DynEvent(type,this);
if (this.hasEventListeners) for(var i=0;i<this.eventListeners.length;i++) {
e.target=this.eventListeners[i].target;
this.eventListeners[i].handleEvent(type,e,args);
}
if(this.higherListener && e.bubble && this.parent) {
e.preBubbleCode();
this.parent.invokeEvent(type,e,args);
}
}
DynObject.prototype.setHigherListener=function(b) {
this.higherListener = b
for(var i in this.children)
this.children[i].setHigherListener(b||this.hasEventListeners)
}
DynObject.prototype.eventListeners = null;
DynObject.prototype._listeners_del = DynObject.prototype.del
DynObject.prototype.del = function() {
this.removeAllEventListeners();
this._listeners_del();
};
--- NEW FILE ---
/*
DynAPI Distribution
Event Classes
The DynAPI Distribution is distributed under the terms of the GNU LGPL license.
Requirements:
dynapi.api [dynlayer, dyndocument, browser]
dynapi.event [listeners]
*/
// The mouseEvent object
DynMouseEvent=function() {
this.DynEvent=DynEvent;
this.DynEvent();
};
DynMouseEvent.prototype=new DynEvent();
DynMouseEvent.prototype.preBubbleCode=function() {
if(!this.src.parent) return;
this.x+=this.src.parent.x;
this.y+=this.src.parent.y;
// This is difficult to explain but needs to be here. Try removing it and
running 'dynapi.event.bubbling.html'
if(is.def && (this.type=='mouseover' || this.type=='mouseout') &&
this.src.parent) this.src = this.src.parent;
};
DynMouseEvent.prototype.getX=function() {return this.x};
DynMouseEvent.prototype.getY=function() {return this.y};
DynMouseEvent.prototype.getPageX=function() {return this.pageX};
DynMouseEvent.prototype.getPageY=function() {return this.pageY};
DynMouseEvent.prototype.cancelBrowserEvent=function(b) {this.browserReturn=false};
// This is THE event.
DynMouseEvent._e=new DynMouseEvent()
DynMouseEvent.EventMethod=function(e) {
var dynobject=this.lyrobj;
if(is.def) {
if (is.ie) var e=dynobject.frame.event
e.cancelBubble=true;
if (DynAPI.wasDragging && e.type=="click") {
DynAPI.wasDragging=false;
return true;
}
}
// Get the real source for the event
if(is.def) var realsrc =
Methods.getContainerLayerOf(is.ie?e.srcElement:e.target) || dynobject
else if(is.ns4) var realsrc=e.target.lyrobj||dynobject
// Now 'realsrc' should point to the DynLayer object where the event initially
was triggered
if (!realsrc) { alert('Error in MouseEvents'); return; }
if(is.def) {
if(e.type=="mouseout" &&
realsrc.isParentOf(Methods.getContainerLayerOf(is.ie?e.toElement:e.relatedTarget),true))
return true;
if(e.type=="mouseover" &&
realsrc.isParentOf(Methods.getContainerLayerOf(is.ie?e.fromElement:e.relatedTarget),true))
return true;
}
var evt=DynMouseEvent._e
// Step one: properties common to all DynEvents
var type = e.type
evt.type = type;
evt.src=realsrc;
evt.browserReturn=true;
evt.bubble=true;
// Step two: mouse coordinate properties
evt.pageX=is.ie?e.x+document.body.scrollLeft:e.pageX-window.pageXOffset;
evt.pageY=is.ie?e.y+document.body.scrollTop:e.pageY-window.pageYOffset;
evt.x=is.ie?e.x-evt.src.getPageX():e.layerX
evt.y=is.ie?e.y-evt.src.getPageY():e.layerY
// Step three: mouse buttons
var b=is.ie?e.button:e.which;
if (is.ie){
if (b==2) b=3;
else if (b==4) b=2;
};
evt.button=b;
if (evt.button==2 && (type=='mousedown' || type=='mouseup' ||
type=='mouseclick')) type=evt.type='md'+type;
if (evt.button==3 && (type=='mousedown' || type=='mouseup' ||
type=='mouseclick')) type=evt.type='rt'+type;
// Step four: modifiers
if (is.def){
evt.altKey=(e.altKey||e.altLeft);
evt.ctrlKey=(e.ctrlKey||e.ctrlLeft);
evt.shiftKey=(e.shiftKey||e.shiftLeft);
}
else if (is.ns4){
var m=e.modifiers;
evt.altKey=(m==1||m==3||m==5||m==7);
evt.ctrlKey=(m==2||m==3||m==6||m==7);
evt.shiftKey=(m==4||m==5||m==6||m==7);
}
// Step five: reference to the original event
evt.orig=e
if(is.def) {
if (evt.type=='mouseover') {
var fromL =
Methods.getContainerLayerOf(is.ie?e.fromElement:e.relatedTarget)
if(fromL && fromL.isChildOf(realsrc.parent,true))
evt.setBubble(false);
}
if (evt.type=='mouseout') {
var toL =
Methods.getContainerLayerOf(is.ie?e.toElement:e.relatedTarget)
if(toL && toL.isChildOf(realsrc.parent,true))
evt.setBubble(false);
}
}
else if(is.ns4 && (e.type=="mouseover" || e.type=="mouseout"))
evt.setBubble(false);
// Invoke the event
realsrc.invokeEvent(type,evt);
if (is.ns4 && is.other && type=="mousedown") {
if (this.dbltimer!=null) {
evt.type=type="dblclick";
evt.bubble = true;
realsrc.invokeEvent(type,evt);
}
else this.dbltimer=setTimeout(this+'.dbltimer=null',300);
}
if (is.ns4) {
if (e.cancelBubble) return;
if (e && e.target.handleEvent && e.target!=this)
e.target.handleEvent(type,e);
}
if (is.ns4 && is.other && type=="mouseup") {
evt.type=type="click";
evt.bubble = true;
realsrc.invokeEvent(type,evt);
}
return evt.browserReturn;
}
// Extend DynDocument to capture its events
DynDocument.prototype.captureMouseEvents = function() {
if(is.def&&!is.ie) {
this.doc.addEventListener("mousemove",DynMouseEvent.EventMethod,false)
this.doc.addEventListener("mousedown",DynMouseEvent.EventMethod,false)
this.doc.addEventListener("mouseup",DynMouseEvent.EventMethod,false)
this.doc.addEventListener("mouseover",DynMouseEvent.EventMethod,false)
this.doc.addEventListener("mouseout",DynMouseEvent.EventMethod,false)
this.doc.addEventListener("click",DynMouseEvent.EventMethod,false)
this.doc.addEventListener("dblclick",DynMouseEvent.EventMethod,false)
}
else {
if (is.ns4) this.doc.captureEvents(Event.MOUSEMOVE | Event.MOUSEDOWN |
Event.MOUSEUP | Event.CLICK | Event.DBLCLICK);
this.doc.onmousemove=this.doc.onmousedown=this.doc.onmouseup=this.doc.onclick=this.doc.ondblclick=DynMouseEvent.EventMethod;
if (is.ie5) this.doc.oncontextmenu=function(){ return false };
}
}
DynDocument.prototype.releaseMouseEvents=function() {
if(is.def&&!is.ie) {
this.doc.removeEventListener("mousemove",DynMouseEvent.EventMethod,false)
this.doc.removeEventListener("mousedown",DynMouseEvent.EventMethod,false)
this.doc.removeEventListener("mouseup",DynMouseEvent.EventMethod,false)
this.doc.removeEventListener("mouseover",DynMouseEvent.EventMethod,false)
this.doc.removeEventListener("mouseout",DynMouseEvent.EventMethod,false)
this.doc.removeEventListener("click",DynMouseEvent.EventMethod,false)
this.doc.removeEventListener("dblclick",DynMouseEvent.EventMethod,false)
}
else {
if (is.ns4) this.doc.releaseEvents(Event.MOUSEMOVE | Event.MOUSEDOWN |
Event.MOUSEUP | Event.CLICK | Event.DBLCLICK);
this.doc.onmousemove=this.doc.onmousedown=this.doc.onmouseup=this.doc.onclick=this.doc.ondblclick=null
if (is.ie5) this.doc.oncontextmenu=null
}
}
/* Extend DynLayer to capture mouse events upon layer creation (if needed) */
DynLayer.prototype.captureMouseEvents = function() {
var elm=this.elm
if(!elm) return
if(is.def&&!is.ie) {
elm.addEventListener("mousemove",DynMouseEvent.EventMethod,false)
elm.addEventListener("mousedown",DynMouseEvent.EventMethod,false)
elm.addEventListener("mouseup",DynMouseEvent.EventMethod,false)
elm.addEventListener("mouseover",DynMouseEvent.EventMethod,false)
elm.addEventListener("mouseout",DynMouseEvent.EventMethod,false)
elm.addEventListener("click",DynMouseEvent.EventMethod,false)
elm.addEventListener("dblclick",DynMouseEvent.EventMethod,false)
}
else {
if (is.ns4) elm.captureEvents(Event.MOUSEDOWN | Event.MOUSEUP |
Event.CLICK | Event.DBLCLICK);
elm.onmousemove=elm.onmousedown=elm.onmouseup=elm.onmouseover=elm.onmouseout=elm.onclick=elm.ondblclick=DynMouseEvent.EventMethod
if (is.ie5) elm.oncontextmenu=function(){return false}
}
}
DynLayer.prototype.releaseMouseEvents=function() {
var elm=this.elm
if(!elm) return
if(is.def&&!is.ie) {
elm.removeEventListener("mousemove",DynMouseEvent.EventMethod,false)
elm.removeEventListener("mousedown",DynMouseEvent.EventMethod,false)
elm.removeEventListener("mouseup",DynMouseEvent.EventMethod,false)
elm.removeEventListener("mouseover",DynMouseEvent.EventMethod,false)
elm.removeEventListener("mouseout",DynMouseEvent.EventMethod,false)
elm.removeEventListener("click",DynMouseEvent.EventMethod,false)
elm.removeEventListener("dblclick",DynMouseEvent.EventMethod,false)
}
else {
if (is.ns4) elm.releaseEvents(Event.MOUSEDOWN | Event.MOUSEUP |
Event.CLICK | Event.DBLCLICK);
elm.onmousemove=elm.onmousedown=elm.onmouseup=elm.onclick=elm.ondblclick=null
if (is.ie5) elm.oncontextmenu=null
}
}
DynObject.prototype.assignMouseEvents = function() {
if (this.hasEventListeners) this.captureMouseEvents()
var l=this.children.length;
for (var i=0; i<l; i++) this.children[i].assignMouseEvents()
}
DynObject.prototype._OldM_addEventListener = DynObject.prototype.addEventListener
DynObject.prototype.addEventListener = function(l) {
var r = this._OldM_addEventListener(l)
if(this.hasEventListeners && this.created) this.captureMouseEvents()
return r
}
DynObject.prototype._OldM_removeEventListener = DynObject.prototype.removeEventListener
DynObject.prototype.removeEventListener = function(l) {
var r = this._OldM_removeEventListener(l)
if(!this.hasEventListeners) this.releaseMouseEvents()
return r
}
DynObject.prototype._OldM_removeAllEventListeners =
DynObject.prototype.removeAllEventListeners
DynObject.prototype.removeAllEventListeners = function() {
var r = this._OldM_removeAllEventListeners()
this.releaseMouseEvents()
return r
}
// DynLayer Specific
DynLayer.prototype._OldM_specificCreate = DynLayer.prototype.specificCreate
DynLayer.prototype.specificCreate = function() {
this._OldM_specificCreate()
this.assignMouseEvents()
}
// DynDocument Specific
DynDocument.prototype._OldM_specificCreate = DynDocument.prototype.specificCreate
DynDocument.prototype.specificCreate = function() {
this._OldM_specificCreate()
this.assignMouseEvents()
}
--- NEW FILE ---
/*
DynAPI Distribution
Simple event classes.
The DynAPI Distribution is distributed under the terms of the GNU LGPL license.
*/
DynObject.prototype.invokeEvent = function(type,e,args) {
var ret = true;
if(this["on"+type]) ret = this["on"+type](e,args)
if(ret && this.parent) this.parent.invokeEvent(type,e,args);
}
_______________________________________________
Dynapi-CVS mailing list
[EMAIL PROTECTED]
http://lists.sourceforge.net/lists/listinfo/dynapi-cvs