You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
232 lines
6.2 KiB
232 lines
6.2 KiB
define([ |
|
"../_base/array", "../_base/declare", "../_base/lang", "../_base/window", |
|
"../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch", |
|
"./common", "./autoscroll", "./Avatar" |
|
], function(array, declare, lang, win, domClass, Evented, has, keys, on, topic, touch, |
|
dnd, autoscroll, Avatar){ |
|
|
|
// module: |
|
// dojo/dnd/Manager |
|
|
|
var Manager = declare("dojo.dnd.Manager", [Evented], { |
|
// summary: |
|
// the manager of DnD operations (usually a singleton) |
|
constructor: function(){ |
|
this.avatar = null; |
|
this.source = null; |
|
this.nodes = []; |
|
this.copy = true; |
|
this.target = null; |
|
this.canDropFlag = false; |
|
this.events = []; |
|
}, |
|
|
|
// avatar's offset from the mouse |
|
OFFSET_X: has("touch") ? 4 : 16, |
|
OFFSET_Y: has("touch") ? 4 : 16, |
|
|
|
// methods |
|
overSource: function(source){ |
|
// summary: |
|
// called when a source detected a mouse-over condition |
|
// source: Object |
|
// the reporter |
|
if(this.avatar){ |
|
this.target = (source && source.targetState != "Disabled") ? source : null; |
|
this.canDropFlag = Boolean(this.target); |
|
this.avatar.update(); |
|
} |
|
topic.publish("/dnd/source/over", source); |
|
}, |
|
outSource: function(source){ |
|
// summary: |
|
// called when a source detected a mouse-out condition |
|
// source: Object |
|
// the reporter |
|
if(this.avatar){ |
|
if(this.target == source){ |
|
this.target = null; |
|
this.canDropFlag = false; |
|
this.avatar.update(); |
|
topic.publish("/dnd/source/over", null); |
|
} |
|
}else{ |
|
topic.publish("/dnd/source/over", null); |
|
} |
|
}, |
|
startDrag: function(source, nodes, copy){ |
|
// summary: |
|
// called to initiate the DnD operation |
|
// source: Object |
|
// the source which provides items |
|
// nodes: Array |
|
// the list of transferred items |
|
// copy: Boolean |
|
// copy items, if true, move items otherwise |
|
|
|
// Tell autoscroll that a drag is starting |
|
autoscroll.autoScrollStart(win.doc); |
|
|
|
this.source = source; |
|
this.nodes = nodes; |
|
this.copy = Boolean(copy); // normalizing to true boolean |
|
this.avatar = this.makeAvatar(); |
|
win.body().appendChild(this.avatar.node); |
|
topic.publish("/dnd/start", source, nodes, this.copy); |
|
|
|
function stopEvent(e){ |
|
e.preventDefault(); |
|
e.stopPropagation(); |
|
} |
|
|
|
this.events = [ |
|
on(win.doc, touch.move, lang.hitch(this, "onMouseMove")), |
|
on(win.doc, touch.release, lang.hitch(this, "onMouseUp")), |
|
on(win.doc, "keydown", lang.hitch(this, "onKeyDown")), |
|
on(win.doc, "keyup", lang.hitch(this, "onKeyUp")), |
|
|
|
// cancel text selection and text dragging |
|
on(win.doc, "dragstart", stopEvent), |
|
on(win.body(), "selectstart", stopEvent) |
|
]; |
|
var c = "dojoDnd" + (copy ? "Copy" : "Move"); |
|
domClass.add(win.body(), c); |
|
}, |
|
canDrop: function(flag){ |
|
// summary: |
|
// called to notify if the current target can accept items |
|
var canDropFlag = Boolean(this.target && flag); |
|
if(this.canDropFlag != canDropFlag){ |
|
this.canDropFlag = canDropFlag; |
|
this.avatar.update(); |
|
} |
|
}, |
|
stopDrag: function(){ |
|
// summary: |
|
// stop the DnD in progress |
|
domClass.remove(win.body(), ["dojoDndCopy", "dojoDndMove"]); |
|
array.forEach(this.events, function(handle){ handle.remove(); }); |
|
this.events = []; |
|
this.avatar.destroy(); |
|
this.avatar = null; |
|
this.source = this.target = null; |
|
this.nodes = []; |
|
}, |
|
makeAvatar: function(){ |
|
// summary: |
|
// makes the avatar; it is separate to be overwritten dynamically, if needed |
|
return new Avatar(this); |
|
}, |
|
updateAvatar: function(){ |
|
// summary: |
|
// updates the avatar; it is separate to be overwritten dynamically, if needed |
|
this.avatar.update(); |
|
}, |
|
|
|
// mouse event processors |
|
onMouseMove: function(e){ |
|
// summary: |
|
// event processor for onmousemove |
|
// e: Event |
|
// mouse event |
|
var a = this.avatar; |
|
if(a){ |
|
autoscroll.autoScrollNodes(e); |
|
//autoscroll.autoScroll(e); |
|
var s = a.node.style; |
|
s.left = (e.pageX + this.OFFSET_X) + "px"; |
|
s.top = (e.pageY + this.OFFSET_Y) + "px"; |
|
var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e))); |
|
if(this.copy != copy){ |
|
this._setCopyStatus(copy); |
|
} |
|
} |
|
if(has("touch")){ |
|
// Prevent page from scrolling so that user can drag instead. |
|
e.preventDefault(); |
|
} |
|
}, |
|
onMouseUp: function(e){ |
|
// summary: |
|
// event processor for onmouseup |
|
// e: Event |
|
// mouse event |
|
if(this.avatar){ |
|
if(this.target && this.canDropFlag){ |
|
var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e))); |
|
topic.publish("/dnd/drop/before", this.source, this.nodes, copy, this.target, e); |
|
topic.publish("/dnd/drop", this.source, this.nodes, copy, this.target, e); |
|
}else{ |
|
topic.publish("/dnd/cancel"); |
|
} |
|
this.stopDrag(); |
|
} |
|
}, |
|
|
|
// keyboard event processors |
|
onKeyDown: function(e){ |
|
// summary: |
|
// event processor for onkeydown: |
|
// watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag |
|
// e: Event |
|
// keyboard event |
|
if(this.avatar){ |
|
switch(e.keyCode){ |
|
case keys.CTRL: |
|
var copy = Boolean(this.source.copyState(true)); |
|
if(this.copy != copy){ |
|
this._setCopyStatus(copy); |
|
} |
|
break; |
|
case keys.ESCAPE: |
|
topic.publish("/dnd/cancel"); |
|
this.stopDrag(); |
|
break; |
|
} |
|
} |
|
}, |
|
onKeyUp: function(e){ |
|
// summary: |
|
// event processor for onkeyup, watching for CTRL for copy/move status |
|
// e: Event |
|
// keyboard event |
|
if(this.avatar && e.keyCode == keys.CTRL){ |
|
var copy = Boolean(this.source.copyState(false)); |
|
if(this.copy != copy){ |
|
this._setCopyStatus(copy); |
|
} |
|
} |
|
}, |
|
|
|
// utilities |
|
_setCopyStatus: function(copy){ |
|
// summary: |
|
// changes the copy status |
|
// copy: Boolean |
|
// the copy status |
|
this.copy = copy; |
|
this.source._markDndStatus(this.copy); |
|
this.updateAvatar(); |
|
domClass.replace(win.body(), |
|
"dojoDnd" + (this.copy ? "Copy" : "Move"), |
|
"dojoDnd" + (this.copy ? "Move" : "Copy")); |
|
} |
|
}); |
|
|
|
// dnd._manager: |
|
// The manager singleton variable. Can be overwritten if needed. |
|
dnd._manager = null; |
|
|
|
Manager.manager = dnd.manager = function(){ |
|
// summary: |
|
// Returns the current DnD manager. Creates one if it is not created yet. |
|
if(!dnd._manager){ |
|
dnd._manager = new Manager(); |
|
} |
|
return dnd._manager; // Object |
|
}; |
|
|
|
// TODO: for 2.0, store _manager and manager in Manager only. Don't access dnd or dojo.dnd. |
|
|
|
return Manager; |
|
});
|
|
|