]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/dnd/Source.js.uncompressed.js
1 define("dojo/dnd/Source", [
2 "../_base/array", "../_base/connect", "../_base/declare", "../_base/kernel", "../_base/lang",
3 "../dom-class", "../dom-geometry", "../mouse", "../ready", "../topic",
4 "./common", "./Selector", "./Manager"
5 ], function(array
, connect
, declare
, kernel
, lang
, domClass
, domGeom
, mouse
, ready
, topic
,
6 dnd
, Selector
, Manager
){
13 "Horizontal"- if this is the horizontal container
16 "Moved" - this source is being moved
17 "Copied" - this source is being copied
20 "Disabled" - the target cannot accept an avatar
22 "" - item is not selected
23 "Before" - insert point is before the anchor
24 "After" - insert point is after the anchor
30 // a dict of parameters for DnD Source configuration. Note that any
31 // property on Source elements may be configured, but this is the
34 // can be used as a DnD source. Defaults to true.
36 // list of accepted types (text strings) for a target; defaults to
39 // if true refreshes the node list on every operation; false by default
41 // copy items, if true, use a state of Ctrl key otherwise,
42 // see selfCopy and selfAccept for more details
44 // the move delay in pixels before detecting a drag; 0 by default
45 // horizontal: Boolean?
46 // a horizontal container, if true, vertical otherwise or when omitted
48 // copy items by default when dropping on itself,
49 // false by default, works only if copyOnly is true
50 // selfAccept: Boolean?
51 // accept its own items when copyOnly is true,
52 // true by default, works only if copyOnly is true
53 // withHandles: Boolean?
54 // allows dragging only by handles, false by default
55 // generateText: Boolean?
56 // generate text node for drag and drop, true by default
60 // For back-compat, remove in 2.0.
63 var requires
= ["dojo/dnd/AutoSource", "dojo/dnd/Target"];
64 require(requires
); // use indirection so modules not rolled into a build
68 var Source
= declare("dojo.dnd.Source", Selector
, {
70 // a Source object, which can be used as a DnD source, or a DnD target
72 // object attributes (for markup)
85 constructor: function(/*DOMNode|String*/ node
, /*__SourceArgs?*/ params
){
87 // a constructor of the Source
89 // node or node's id to build the source on
91 // any property of this class may be configured via the params
92 // object which is mixed-in to the `dojo/dnd/Source` instance
93 lang
.mixin(this, lang
.mixin({}, params
));
94 var type
= this.accept
;
97 for(var i
= 0; i
< type
.length
; ++i
){
98 this.accept
[type
[i
]] = 1;
101 // class-specific variables
102 this.isDragging
= false;
103 this.mouseDown
= false;
104 this.targetAnchor
= null;
105 this.targetBox
= null;
110 this.sourceState
= "";
112 domClass
.add(this.node
, "dojoDndSource");
114 this.targetState
= "";
116 domClass
.add(this.node
, "dojoDndTarget");
119 domClass
.add(this.node
, "dojoDndHorizontal");
123 topic
.subscribe("/dnd/source/over", lang
.hitch(this, "onDndSourceOver")),
124 topic
.subscribe("/dnd/start", lang
.hitch(this, "onDndStart")),
125 topic
.subscribe("/dnd/drop", lang
.hitch(this, "onDndDrop")),
126 topic
.subscribe("/dnd/cancel", lang
.hitch(this, "onDndCancel"))
131 checkAcceptance: function(source
, nodes
){
133 // checks if the target can accept nodes from this source
135 // the source which provides items
137 // the list of transferred items
139 return !this.copyOnly
|| this.selfAccept
;
141 for(var i
= 0; i
< nodes
.length
; ++i
){
142 var type
= source
.getItem(nodes
[i
].id
).type
;
143 // type instanceof Array
145 for(var j
= 0; j
< type
.length
; ++j
){
146 if(type
[j
] in this.accept
){
152 return false; // Boolean
155 return true; // Boolean
157 copyState: function(keyPressed
, self
){
159 // Returns true if we need to copy items, false to move.
160 // It is separated to be overwritten dynamically, if needed.
161 // keyPressed: Boolean
162 // the "copy" key was pressed
164 // optional flag that means that we are about to drop on itself
166 if(keyPressed
){ return true; }
167 if(arguments
.length
< 2){
168 self
= this == Manager
.manager().target
;
172 return this.selfCopy
;
175 return this.copyOnly
;
177 return false; // Boolean
181 // prepares the object to be garbage-collected
182 Source
.superclass
.destroy
.call(this);
183 array
.forEach(this.topics
, function(t
){t
.remove();});
184 this.targetAnchor
= null;
187 // mouse event processors
188 onMouseMove: function(e
){
190 // event processor for onmousemove
193 if(this.isDragging
&& this.targetState
== "Disabled"){ return; }
194 Source
.superclass
.onMouseMove
.call(this, e
);
195 var m
= Manager
.manager();
196 if(!this.isDragging
){
197 if(this.mouseDown
&& this.isSource
&&
198 (Math
.abs(e
.pageX
- this._lastX
) > this.delay
|| Math
.abs(e
.pageY
- this._lastY
) > this.delay
)){
199 var nodes
= this.getSelectedNodes();
201 m
.startDrag(this, nodes
, this.copyState(dnd
.getCopyKeyState(e
), true));
206 // calculate before/after
209 if(!this.targetBox
|| this.targetAnchor
!= this.current
){
210 this.targetBox
= domGeom
.position(this.current
, true);
213 // In LTR mode, the left part of the object means "before", but in RTL mode it means "after".
214 before
= (e
.pageX
- this.targetBox
.x
< this.targetBox
.w
/ 2) == domGeom
.isBodyLtr(this.current
.ownerDocument
);
216 before
= (e
.pageY
- this.targetBox
.y
) < (this.targetBox
.h
/ 2);
219 if(this.current
!= this.targetAnchor
|| before
!= this.before
){
220 this._markTargetAnchor(before
);
221 m
.canDrop(!this.current
|| m
.source
!= this || !(this.current
.id
in this.selection
));
225 onMouseDown: function(e
){
227 // event processor for onmousedown
230 if(!this.mouseDown
&& this._legalMouseDown(e
) && (!this.skipForm
|| !dnd
.isFormElement(e
))){
231 this.mouseDown
= true;
232 this._lastX
= e
.pageX
;
233 this._lastY
= e
.pageY
;
234 Source
.superclass
.onMouseDown
.call(this, e
);
237 onMouseUp: function(e
){
239 // event processor for onmouseup
243 this.mouseDown
= false;
244 Source
.superclass
.onMouseUp
.call(this, e
);
248 // topic event processors
249 onDndSourceOver: function(source
){
251 // topic event processor for /dnd/source/over, called when detected a current source
253 // the source which has the mouse over it
255 this.mouseDown
= false;
256 if(this.targetAnchor
){
257 this._unmarkTargetAnchor();
259 }else if(this.isDragging
){
260 var m
= Manager
.manager();
261 m
.canDrop(this.targetState
!= "Disabled" && (!this.current
|| m
.source
!= this || !(this.current
.id
in this.selection
)));
264 onDndStart: function(source
, nodes
, copy
){
266 // topic event processor for /dnd/start, called to initiate the DnD operation
268 // the source which provides items
270 // the list of transferred items
272 // copy items, if true, move items otherwise
273 if(this.autoSync
){ this.sync(); }
275 this._changeState("Source", this == source
? (copy
? "Copied" : "Moved") : "");
277 var accepted
= this.accept
&& this.checkAcceptance(source
, nodes
);
278 this._changeState("Target", accepted
? "" : "Disabled");
280 Manager
.manager().overSource(this);
282 this.isDragging
= true;
284 onDndDrop: function(source
, nodes
, copy
, target
){
286 // topic event processor for /dnd/drop, called to finish the DnD operation
288 // the source which provides items
290 // the list of transferred items
292 // copy items, if true, move items otherwise
294 // the target which accepts items
296 // this one is for us => move nodes!
297 this.onDrop(source
, nodes
, copy
);
301 onDndCancel: function(){
303 // topic event processor for /dnd/cancel, called to cancel the DnD operation
304 if(this.targetAnchor
){
305 this._unmarkTargetAnchor();
306 this.targetAnchor
= null;
309 this.isDragging
= false;
310 this.mouseDown
= false;
311 this._changeState("Source", "");
312 this._changeState("Target", "");
316 onDrop: function(source
, nodes
, copy
){
318 // called only on the current target, when drop is performed
320 // the source which provides items
322 // the list of transferred items
324 // copy items, if true, move items otherwise
327 this.onDropExternal(source
, nodes
, copy
);
329 this.onDropInternal(nodes
, copy
);
332 onDropExternal: function(source
, nodes
, copy
){
334 // called only on the current target, when drop is performed
335 // from an external source
337 // the source which provides items
339 // the list of transferred items
341 // copy items, if true, move items otherwise
343 var oldCreator
= this._normalizedCreator
;
344 // transferring nodes from the source to the target
346 // use defined creator
347 this._normalizedCreator = function(node
, hint
){
348 return oldCreator
.call(this, source
.getItem(node
.id
).data
, hint
);
351 // we have no creator defined => move/clone nodes
354 this._normalizedCreator = function(node
/*=====, hint =====*/){
355 var t
= source
.getItem(node
.id
);
356 var n
= node
.cloneNode(true);
357 n
.id
= dnd
.getUniqueId();
358 return {node
: n
, data
: t
.data
, type
: t
.type
};
362 this._normalizedCreator = function(node
/*=====, hint =====*/){
363 var t
= source
.getItem(node
.id
);
364 source
.delItem(node
.id
);
365 return {node
: node
, data
: t
.data
, type
: t
.type
};
370 if(!copy
&& !this.creator
){
373 this.insertNodes(true, nodes
, this.before
, this.current
);
374 if(!copy
&& this.creator
){
375 source
.deleteSelectedNodes();
377 this._normalizedCreator
= oldCreator
;
379 onDropInternal: function(nodes
, copy
){
381 // called only on the current target, when drop is performed
382 // from the same target/source
384 // the list of transferred items
386 // copy items, if true, move items otherwise
388 var oldCreator
= this._normalizedCreator
;
389 // transferring nodes within the single source
390 if(this.current
&& this.current
.id
in this.selection
){
396 // create new copies of data items
397 this._normalizedCreator = function(node
, hint
){
398 return oldCreator
.call(this, this.getItem(node
.id
).data
, hint
);
402 this._normalizedCreator = function(node
/*=====, hint =====*/){
403 var t
= this.getItem(node
.id
);
404 var n
= node
.cloneNode(true);
405 n
.id
= dnd
.getUniqueId();
406 return {node
: n
, data
: t
.data
, type
: t
.type
};
415 this._normalizedCreator = function(node
/*=====, hint =====*/){
416 var t
= this.getItem(node
.id
);
417 return {node
: node
, data
: t
.data
, type
: t
.type
};
420 this._removeSelection();
421 this.insertNodes(true, nodes
, this.before
, this.current
);
422 this._normalizedCreator
= oldCreator
;
424 onDraggingOver: function(){
426 // called during the active DnD operation, when items
427 // are dragged over this target, and it is not disabled
429 onDraggingOut: function(){
431 // called during the active DnD operation, when items
432 // are dragged away from this target, and it is not disabled
436 onOverEvent: function(){
438 // this function is called once, when mouse is over our container
439 Source
.superclass
.onOverEvent
.call(this);
440 Manager
.manager().overSource(this);
441 if(this.isDragging
&& this.targetState
!= "Disabled"){
442 this.onDraggingOver();
445 onOutEvent: function(){
447 // this function is called once, when mouse is out of our container
448 Source
.superclass
.onOutEvent
.call(this);
449 Manager
.manager().outSource(this);
450 if(this.isDragging
&& this.targetState
!= "Disabled"){
451 this.onDraggingOut();
454 _markTargetAnchor: function(before
){
456 // assigns a class to the current target anchor based on "before" status
458 // insert before, if true, after otherwise
459 if(this.current
== this.targetAnchor
&& this.before
== before
){ return; }
460 if(this.targetAnchor
){
461 this._removeItemClass(this.targetAnchor
, this.before
? "Before" : "After");
463 this.targetAnchor
= this.current
;
464 this.targetBox
= null;
465 this.before
= before
;
466 if(this.targetAnchor
){
467 this._addItemClass(this.targetAnchor
, this.before
? "Before" : "After");
470 _unmarkTargetAnchor: function(){
472 // removes a class of the current target anchor based on "before" status
473 if(!this.targetAnchor
){ return; }
474 this._removeItemClass(this.targetAnchor
, this.before
? "Before" : "After");
475 this.targetAnchor
= null;
476 this.targetBox
= null;
479 _markDndStatus: function(copy
){
481 // changes source's state based on "copy" status
482 this._changeState("Source", copy
? "Copied" : "Moved");
484 _legalMouseDown: function(e
){
486 // checks if user clicked on "approved" items
490 // accept only the left mouse button, or the left finger
491 if(e
.type
!= "touchstart" && !mouse
.isLeft(e
)){ return false; }
493 if(!this.withHandles
){ return true; }
496 for(var node
= e
.target
; node
&& node
!== this.node
; node
= node
.parentNode
){
497 if(domClass
.contains(node
, "dojoDndHandle")){ return true; }
498 if(domClass
.contains(node
, "dojoDndItem") || domClass
.contains(node
, "dojoDndIgnore")){ break; }
500 return false; // Boolean