dojo.provide("dojo.dnd.Manager"); dojo.require("dojo.dnd.common"); dojo.require("dojo.dnd.autoscroll"); dojo.require("dojo.dnd.Avatar"); dojo.declare("dojo.dnd.Manager", null, { // 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: 16, OFFSET_Y: 16, // methods overSource: function(source){ // summary: called when a source detected a mouse-over conditiion // source: Object: the reporter if(this.avatar){ this.target = (source && source.targetState != "Disabled") ? source : null; this.canDropFlag = Boolean(this.target); this.avatar.update(); } dojo.publish("/dnd/source/over", [source]); }, outSource: function(source){ // summary: called when a source detected a mouse-out conditiion // source: Object: the reporter if(this.avatar){ if(this.target == source){ this.target = null; this.canDropFlag = false; this.avatar.update(); dojo.publish("/dnd/source/over", [null]); } }else{ dojo.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 this.source = source; this.nodes = nodes; this.copy = Boolean(copy); // normalizing to true boolean this.avatar = this.makeAvatar(); dojo.body().appendChild(this.avatar.node); dojo.publish("/dnd/start", [source, nodes, this.copy]); this.events = [ dojo.connect(dojo.doc, "onmousemove", this, "onMouseMove"), dojo.connect(dojo.doc, "onmouseup", this, "onMouseUp"), dojo.connect(dojo.doc, "onkeydown", this, "onKeyDown"), dojo.connect(dojo.doc, "onkeyup", this, "onKeyUp"), // cancel text selection and text dragging dojo.connect(dojo.doc, "ondragstart", dojo.stopEvent), dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent) ]; var c = "dojoDnd" + (copy ? "Copy" : "Move"); dojo.addClass(dojo.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 dojo.removeClass(dojo.body(), "dojoDndCopy"); dojo.removeClass(dojo.body(), "dojoDndMove"); dojo.forEach(this.events, dojo.disconnect); 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 dojo.dnd.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){ dojo.dnd.autoScrollNodes(e); //dojo.dnd.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(dojo.dnd.getCopyKeyState(e))); if(this.copy != copy){ this._setCopyStatus(copy); } } }, onMouseUp: function(e){ // summary: event processor for onmouseup // e: Event: mouse event if(this.avatar && (!("mouseButton" in this.source) || (dojo.isSafari && dojo.dnd._isMac && this.source.mouseButton == 2 ? e.button == 0 : this.source.mouseButton == e.button))){ if(this.target && this.canDropFlag){ var copy = Boolean(this.source.copyState(dojo.dnd.getCopyKeyState(e))), params = [this.source, this.nodes, copy, this.target]; dojo.publish("/dnd/drop/before", params); dojo.publish("/dnd/drop", params); }else{ dojo.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 dojo.keys.CTRL: var copy = Boolean(this.source.copyState(true)); if(this.copy != copy){ this._setCopyStatus(copy); } break; case dojo.keys.ESCAPE: dojo.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 == dojo.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(); dojo.removeClass(dojo.body(), "dojoDnd" + (this.copy ? "Move" : "Copy")); dojo.addClass(dojo.body(), "dojoDnd" + (this.copy ? "Copy" : "Move")); } }); // summary: the manager singleton variable, can be overwritten, if needed dojo.dnd._manager = null; dojo.dnd.manager = function(){ // summary: returns the current DnD manager, creates one if it is not created yet if(!dojo.dnd._manager){ dojo.dnd._manager = new dojo.dnd.Manager(); } return dojo.dnd._manager; // Object };