]> git.wh0rd.org - tt-rss.git/blob - lib/dojo/dnd/Source.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dojo / dnd / Source.js.uncompressed.js
1 define("dojo/dnd/Source", ["../main", "./Selector", "./Manager"], function(dojo, Selector, Manager) {
2 // module:
3 // dojo/dnd/Source
4 // summary:
5 // TODOC
6
7 /*=====
8 Selector = dojo.dnd.Selector;
9 =====*/
10
11 /*
12 Container property:
13 "Horizontal"- if this is the horizontal container
14 Source states:
15 "" - normal state
16 "Moved" - this source is being moved
17 "Copied" - this source is being copied
18 Target states:
19 "" - normal state
20 "Disabled" - the target cannot accept an avatar
21 Target anchor state:
22 "" - item is not selected
23 "Before" - insert point is before the anchor
24 "After" - insert point is after the anchor
25 */
26
27 /*=====
28 dojo.dnd.__SourceArgs = function(){
29 // summary:
30 // a dict of parameters for DnD Source configuration. Note that any
31 // property on Source elements may be configured, but this is the
32 // short-list
33 // isSource: Boolean?
34 // can be used as a DnD source. Defaults to true.
35 // accept: Array?
36 // list of accepted types (text strings) for a target; defaults to
37 // ["text"]
38 // autoSync: Boolean
39 // if true refreshes the node list on every operation; false by default
40 // copyOnly: Boolean?
41 // copy items, if true, use a state of Ctrl key otherwise,
42 // see selfCopy and selfAccept for more details
43 // delay: Number
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
47 // selfCopy: Boolean?
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
57 this.isSource = isSource;
58 this.accept = accept;
59 this.autoSync = autoSync;
60 this.copyOnly = copyOnly;
61 this.delay = delay;
62 this.horizontal = horizontal;
63 this.selfCopy = selfCopy;
64 this.selfAccept = selfAccept;
65 this.withHandles = withHandles;
66 this.generateText = true;
67 }
68 =====*/
69
70 // For back-compat, remove in 2.0.
71 if(!dojo.isAsync){
72 dojo.ready(0, function(){
73 var requires = ["dojo/dnd/AutoSource", "dojo/dnd/Target"];
74 require(requires); // use indirection so modules not rolled into a build
75 })
76 }
77
78 return dojo.declare("dojo.dnd.Source", Selector, {
79 // summary:
80 // a Source object, which can be used as a DnD source, or a DnD target
81
82 // object attributes (for markup)
83 isSource: true,
84 horizontal: false,
85 copyOnly: false,
86 selfCopy: false,
87 selfAccept: true,
88 skipForm: false,
89 withHandles: false,
90 autoSync: false,
91 delay: 0, // pixels
92 accept: ["text"],
93 generateText: true,
94
95 constructor: function(/*DOMNode|String*/node, /*dojo.dnd.__SourceArgs?*/params){
96 // summary:
97 // a constructor of the Source
98 // node:
99 // node or node's id to build the source on
100 // params:
101 // any property of this class may be configured via the params
102 // object which is mixed-in to the `dojo.dnd.Source` instance
103 dojo.mixin(this, dojo.mixin({}, params));
104 var type = this.accept;
105 if(type.length){
106 this.accept = {};
107 for(var i = 0; i < type.length; ++i){
108 this.accept[type[i]] = 1;
109 }
110 }
111 // class-specific variables
112 this.isDragging = false;
113 this.mouseDown = false;
114 this.targetAnchor = null;
115 this.targetBox = null;
116 this.before = true;
117 this._lastX = 0;
118 this._lastY = 0;
119 // states
120 this.sourceState = "";
121 if(this.isSource){
122 dojo.addClass(this.node, "dojoDndSource");
123 }
124 this.targetState = "";
125 if(this.accept){
126 dojo.addClass(this.node, "dojoDndTarget");
127 }
128 if(this.horizontal){
129 dojo.addClass(this.node, "dojoDndHorizontal");
130 }
131 // set up events
132 this.topics = [
133 dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
134 dojo.subscribe("/dnd/start", this, "onDndStart"),
135 dojo.subscribe("/dnd/drop", this, "onDndDrop"),
136 dojo.subscribe("/dnd/cancel", this, "onDndCancel")
137 ];
138 },
139
140 // methods
141 checkAcceptance: function(source, nodes){
142 // summary:
143 // checks if the target can accept nodes from this source
144 // source: Object
145 // the source which provides items
146 // nodes: Array
147 // the list of transferred items
148 if(this == source){
149 return !this.copyOnly || this.selfAccept;
150 }
151 for(var i = 0; i < nodes.length; ++i){
152 var type = source.getItem(nodes[i].id).type;
153 // type instanceof Array
154 var flag = false;
155 for(var j = 0; j < type.length; ++j){
156 if(type[j] in this.accept){
157 flag = true;
158 break;
159 }
160 }
161 if(!flag){
162 return false; // Boolean
163 }
164 }
165 return true; // Boolean
166 },
167 copyState: function(keyPressed, self){
168 // summary:
169 // Returns true if we need to copy items, false to move.
170 // It is separated to be overwritten dynamically, if needed.
171 // keyPressed: Boolean
172 // the "copy" key was pressed
173 // self: Boolean?
174 // optional flag that means that we are about to drop on itself
175
176 if(keyPressed){ return true; }
177 if(arguments.length < 2){
178 self = this == Manager.manager().target;
179 }
180 if(self){
181 if(this.copyOnly){
182 return this.selfCopy;
183 }
184 }else{
185 return this.copyOnly;
186 }
187 return false; // Boolean
188 },
189 destroy: function(){
190 // summary:
191 // prepares the object to be garbage-collected
192 dojo.dnd.Source.superclass.destroy.call(this);
193 dojo.forEach(this.topics, dojo.unsubscribe);
194 this.targetAnchor = null;
195 },
196
197 // mouse event processors
198 onMouseMove: function(e){
199 // summary:
200 // event processor for onmousemove
201 // e: Event
202 // mouse event
203 if(this.isDragging && this.targetState == "Disabled"){ return; }
204 dojo.dnd.Source.superclass.onMouseMove.call(this, e);
205 var m = Manager.manager();
206 if(!this.isDragging){
207 if(this.mouseDown && this.isSource &&
208 (Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay)){
209 var nodes = this.getSelectedNodes();
210 if(nodes.length){
211 m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e), true));
212 }
213 }
214 }
215 if(this.isDragging){
216 // calculate before/after
217 var before = false;
218 if(this.current){
219 if(!this.targetBox || this.targetAnchor != this.current){
220 this.targetBox = dojo.position(this.current, true);
221 }
222 if(this.horizontal){
223 before = (e.pageX - this.targetBox.x) < (this.targetBox.w / 2);
224 }else{
225 before = (e.pageY - this.targetBox.y) < (this.targetBox.h / 2);
226 }
227 }
228 if(this.current != this.targetAnchor || before != this.before){
229 this._markTargetAnchor(before);
230 m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
231 }
232 }
233 },
234 onMouseDown: function(e){
235 // summary:
236 // event processor for onmousedown
237 // e: Event
238 // mouse event
239 if(!this.mouseDown && this._legalMouseDown(e) && (!this.skipForm || !dojo.dnd.isFormElement(e))){
240 this.mouseDown = true;
241 this._lastX = e.pageX;
242 this._lastY = e.pageY;
243 dojo.dnd.Source.superclass.onMouseDown.call(this, e);
244 }
245 },
246 onMouseUp: function(e){
247 // summary:
248 // event processor for onmouseup
249 // e: Event
250 // mouse event
251 if(this.mouseDown){
252 this.mouseDown = false;
253 dojo.dnd.Source.superclass.onMouseUp.call(this, e);
254 }
255 },
256
257 // topic event processors
258 onDndSourceOver: function(source){
259 // summary:
260 // topic event processor for /dnd/source/over, called when detected a current source
261 // source: Object
262 // the source which has the mouse over it
263 if(this != source){
264 this.mouseDown = false;
265 if(this.targetAnchor){
266 this._unmarkTargetAnchor();
267 }
268 }else if(this.isDragging){
269 var m = Manager.manager();
270 m.canDrop(this.targetState != "Disabled" && (!this.current || m.source != this || !(this.current.id in this.selection)));
271 }
272 },
273 onDndStart: function(source, nodes, copy){
274 // summary:
275 // topic event processor for /dnd/start, called to initiate the DnD operation
276 // source: Object
277 // the source which provides items
278 // nodes: Array
279 // the list of transferred items
280 // copy: Boolean
281 // copy items, if true, move items otherwise
282 if(this.autoSync){ this.sync(); }
283 if(this.isSource){
284 this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
285 }
286 var accepted = this.accept && this.checkAcceptance(source, nodes);
287 this._changeState("Target", accepted ? "" : "Disabled");
288 if(this == source){
289 Manager.manager().overSource(this);
290 }
291 this.isDragging = true;
292 },
293 onDndDrop: function(source, nodes, copy, target){
294 // summary:
295 // topic event processor for /dnd/drop, called to finish the DnD operation
296 // source: Object
297 // the source which provides items
298 // nodes: Array
299 // the list of transferred items
300 // copy: Boolean
301 // copy items, if true, move items otherwise
302 // target: Object
303 // the target which accepts items
304 if(this == target){
305 // this one is for us => move nodes!
306 this.onDrop(source, nodes, copy);
307 }
308 this.onDndCancel();
309 },
310 onDndCancel: function(){
311 // summary:
312 // topic event processor for /dnd/cancel, called to cancel the DnD operation
313 if(this.targetAnchor){
314 this._unmarkTargetAnchor();
315 this.targetAnchor = null;
316 }
317 this.before = true;
318 this.isDragging = false;
319 this.mouseDown = false;
320 this._changeState("Source", "");
321 this._changeState("Target", "");
322 },
323
324 // local events
325 onDrop: function(source, nodes, copy){
326 // summary:
327 // called only on the current target, when drop is performed
328 // source: Object
329 // the source which provides items
330 // nodes: Array
331 // the list of transferred items
332 // copy: Boolean
333 // copy items, if true, move items otherwise
334
335 if(this != source){
336 this.onDropExternal(source, nodes, copy);
337 }else{
338 this.onDropInternal(nodes, copy);
339 }
340 },
341 onDropExternal: function(source, nodes, copy){
342 // summary:
343 // called only on the current target, when drop is performed
344 // from an external source
345 // source: Object
346 // the source which provides items
347 // nodes: Array
348 // the list of transferred items
349 // copy: Boolean
350 // copy items, if true, move items otherwise
351
352 var oldCreator = this._normalizedCreator;
353 // transferring nodes from the source to the target
354 if(this.creator){
355 // use defined creator
356 this._normalizedCreator = function(node, hint){
357 return oldCreator.call(this, source.getItem(node.id).data, hint);
358 };
359 }else{
360 // we have no creator defined => move/clone nodes
361 if(copy){
362 // clone nodes
363 this._normalizedCreator = function(node, hint){
364 var t = source.getItem(node.id);
365 var n = node.cloneNode(true);
366 n.id = dojo.dnd.getUniqueId();
367 return {node: n, data: t.data, type: t.type};
368 };
369 }else{
370 // move nodes
371 this._normalizedCreator = function(node, hint){
372 var t = source.getItem(node.id);
373 source.delItem(node.id);
374 return {node: node, data: t.data, type: t.type};
375 };
376 }
377 }
378 this.selectNone();
379 if(!copy && !this.creator){
380 source.selectNone();
381 }
382 this.insertNodes(true, nodes, this.before, this.current);
383 if(!copy && this.creator){
384 source.deleteSelectedNodes();
385 }
386 this._normalizedCreator = oldCreator;
387 },
388 onDropInternal: function(nodes, copy){
389 // summary:
390 // called only on the current target, when drop is performed
391 // from the same target/source
392 // nodes: Array
393 // the list of transferred items
394 // copy: Boolean
395 // copy items, if true, move items otherwise
396
397 var oldCreator = this._normalizedCreator;
398 // transferring nodes within the single source
399 if(this.current && this.current.id in this.selection){
400 // do nothing
401 return;
402 }
403 if(copy){
404 if(this.creator){
405 // create new copies of data items
406 this._normalizedCreator = function(node, hint){
407 return oldCreator.call(this, this.getItem(node.id).data, hint);
408 };
409 }else{
410 // clone nodes
411 this._normalizedCreator = function(node, hint){
412 var t = this.getItem(node.id);
413 var n = node.cloneNode(true);
414 n.id = dojo.dnd.getUniqueId();
415 return {node: n, data: t.data, type: t.type};
416 };
417 }
418 }else{
419 // move nodes
420 if(!this.current){
421 // do nothing
422 return;
423 }
424 this._normalizedCreator = function(node, hint){
425 var t = this.getItem(node.id);
426 return {node: node, data: t.data, type: t.type};
427 };
428 }
429 this._removeSelection();
430 this.insertNodes(true, nodes, this.before, this.current);
431 this._normalizedCreator = oldCreator;
432 },
433 onDraggingOver: function(){
434 // summary:
435 // called during the active DnD operation, when items
436 // are dragged over this target, and it is not disabled
437 },
438 onDraggingOut: function(){
439 // summary:
440 // called during the active DnD operation, when items
441 // are dragged away from this target, and it is not disabled
442 },
443
444 // utilities
445 onOverEvent: function(){
446 // summary:
447 // this function is called once, when mouse is over our container
448 dojo.dnd.Source.superclass.onOverEvent.call(this);
449 Manager.manager().overSource(this);
450 if(this.isDragging && this.targetState != "Disabled"){
451 this.onDraggingOver();
452 }
453 },
454 onOutEvent: function(){
455 // summary:
456 // this function is called once, when mouse is out of our container
457 dojo.dnd.Source.superclass.onOutEvent.call(this);
458 Manager.manager().outSource(this);
459 if(this.isDragging && this.targetState != "Disabled"){
460 this.onDraggingOut();
461 }
462 },
463 _markTargetAnchor: function(before){
464 // summary:
465 // assigns a class to the current target anchor based on "before" status
466 // before: Boolean
467 // insert before, if true, after otherwise
468 if(this.current == this.targetAnchor && this.before == before){ return; }
469 if(this.targetAnchor){
470 this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
471 }
472 this.targetAnchor = this.current;
473 this.targetBox = null;
474 this.before = before;
475 if(this.targetAnchor){
476 this._addItemClass(this.targetAnchor, this.before ? "Before" : "After");
477 }
478 },
479 _unmarkTargetAnchor: function(){
480 // summary:
481 // removes a class of the current target anchor based on "before" status
482 if(!this.targetAnchor){ return; }
483 this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
484 this.targetAnchor = null;
485 this.targetBox = null;
486 this.before = true;
487 },
488 _markDndStatus: function(copy){
489 // summary:
490 // changes source's state based on "copy" status
491 this._changeState("Source", copy ? "Copied" : "Moved");
492 },
493 _legalMouseDown: function(e){
494 // summary:
495 // checks if user clicked on "approved" items
496 // e: Event
497 // mouse event
498
499 // accept only the left mouse button
500 if(!dojo.mouseButtons.isLeft(e)){ return false; }
501
502 if(!this.withHandles){ return true; }
503
504 // check for handles
505 for(var node = e.target; node && node !== this.node; node = node.parentNode){
506 if(dojo.hasClass(node, "dojoDndHandle")){ return true; }
507 if(dojo.hasClass(node, "dojoDndItem") || dojo.hasClass(node, "dojoDndIgnore")){ break; }
508 }
509 return false; // Boolean
510 }
511 });
512
513 });