UNPKG

dojo

Version:

Dojo core is a powerful, lightweight library that makes common tasks quicker and easier. Animate elements, manipulate the DOM, and query with easy CSS syntax, all without sacrificing performance.

233 lines (216 loc) 6.3 kB
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; });