Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojo.dnd.Source"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojo.dnd.Source"] = true;
3
dojo.provide("dojo.dnd.Source");
4
 
5
dojo.require("dojo.dnd.Selector");
6
dojo.require("dojo.dnd.Manager");
7
 
8
/*
9
	Container property:
10
		"Horizontal"- if this is the horizontal container
11
	Source states:
12
		""			- normal state
13
		"Moved"		- this source is being moved
14
		"Copied"	- this source is being copied
15
	Target states:
16
		""			- normal state
17
		"Disabled"	- the target cannot accept an avatar
18
	Target anchor state:
19
		""			- item is not selected
20
		"Before"	- insert point is before the anchor
21
		"After"		- insert point is after the anchor
22
*/
23
 
24
dojo.declare("dojo.dnd.Source", dojo.dnd.Selector, {
25
	// summary: a Source object, which can be used as a DnD source, or a DnD target
26
 
27
	// object attributes (for markup)
28
	isSource: true,
29
	horizontal: false,
30
	copyOnly: false,
31
	skipForm: false,
32
	withHandles: false,
33
	accept: ["text"],
34
 
35
	constructor: function(node, params){
36
		// summary: a constructor of the Source
37
		// node: Node: node or node's id to build the source on
38
		// params: Object: a dict of parameters, recognized parameters are:
39
		//	isSource: Boolean: can be used as a DnD source, if true; assumed to be "true" if omitted
40
		//	accept: Array: list of accepted types (text strings) for a target; assumed to be ["text"] if omitted
41
		//	horizontal: Boolean: a horizontal container, if true, vertical otherwise or when omitted
42
		//	copyOnly: Boolean: always copy items, if true, use a state of Ctrl key otherwise
43
		//	withHandles: Boolean: allows dragging only by handles
44
		//	the rest of parameters are passed to the selector
45
		if(!params){ params = {}; }
46
		this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
47
		var type = params.accept instanceof Array ? params.accept : ["text"];
48
		this.accept = null;
49
		if(type.length){
50
			this.accept = {};
51
			for(var i = 0; i < type.length; ++i){
52
				this.accept[type[i]] = 1;
53
			}
54
		}
55
		this.horizontal = params.horizontal;
56
		this.copyOnly = params.copyOnly;
57
		this.withHandles = params.withHandles;
58
		// class-specific variables
59
		this.isDragging = false;
60
		this.mouseDown = false;
61
		this.targetAnchor = null;
62
		this.targetBox = null;
63
		this.before = true;
64
		// states
65
		this.sourceState  = "";
66
		if(this.isSource){
67
			dojo.addClass(this.node, "dojoDndSource");
68
		}
69
		this.targetState  = "";
70
		if(this.accept){
71
			dojo.addClass(this.node, "dojoDndTarget");
72
		}
73
		if(this.horizontal){
74
			dojo.addClass(this.node, "dojoDndHorizontal");
75
		}
76
		// set up events
77
		this.topics = [
78
			dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
79
			dojo.subscribe("/dnd/start",  this, "onDndStart"),
80
			dojo.subscribe("/dnd/drop",   this, "onDndDrop"),
81
			dojo.subscribe("/dnd/cancel", this, "onDndCancel")
82
		];
83
	},
84
 
85
	// methods
86
	checkAcceptance: function(source, nodes){
87
		// summary: checks, if the target can accept nodes from this source
88
		// source: Object: the source which provides items
89
		// nodes: Array: the list of transferred items
90
		if(this == source){ return true; }
91
		for(var i = 0; i < nodes.length; ++i){
92
			var type = source.getItem(nodes[i].id).type;
93
			// type instanceof Array
94
			var flag = false;
95
			for(var j = 0; j < type.length; ++j){
96
				if(type[j] in this.accept){
97
					flag = true;
98
					break;
99
				}
100
			}
101
			if(!flag){
102
				return false;	// Boolean
103
			}
104
		}
105
		return true;	// Boolean
106
	},
107
	copyState: function(keyPressed){
108
		// summary: Returns true, if we need to copy items, false to move.
109
		//		It is separated to be overwritten dynamically, if needed.
110
		// keyPressed: Boolean: the "copy" was pressed
111
		return this.copyOnly || keyPressed;	// Boolean
112
	},
113
	destroy: function(){
114
		// summary: prepares the object to be garbage-collected
115
		dojo.dnd.Source.superclass.destroy.call(this);
116
		dojo.forEach(this.topics, dojo.unsubscribe);
117
		this.targetAnchor = null;
118
	},
119
 
120
	// markup methods
121
	markupFactory: function(params, node){
122
		params._skipStartup = true;
123
		return new dojo.dnd.Source(node, params);
124
	},
125
 
126
	// mouse event processors
127
	onMouseMove: function(e){
128
		// summary: event processor for onmousemove
129
		// e: Event: mouse event
130
		if(this.isDragging && this.targetState == "Disabled"){ return; }
131
		dojo.dnd.Source.superclass.onMouseMove.call(this, e);
132
		var m = dojo.dnd.manager();
133
		if(this.isDragging){
134
			// calculate before/after
135
			var before = false;
136
			if(this.current){
137
				if(!this.targetBox || this.targetAnchor != this.current){
138
					this.targetBox = {
139
						xy: dojo.coords(this.current, true),
140
						w: this.current.offsetWidth,
141
						h: this.current.offsetHeight
142
					};
143
				}
144
				if(this.horizontal){
145
					before = (e.pageX - this.targetBox.xy.x) < (this.targetBox.w / 2);
146
				}else{
147
					before = (e.pageY - this.targetBox.xy.y) < (this.targetBox.h / 2);
148
				}
149
			}
150
			if(this.current != this.targetAnchor || before != this.before){
151
				this._markTargetAnchor(before);
152
				m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
153
			}
154
		}else{
155
			if(this.mouseDown && this.isSource){
156
				var nodes = this.getSelectedNodes();
157
				if(nodes.length){
158
					m.startDrag(this, nodes, this.copyState(dojo.dnd.getCopyKeyState(e)));
159
				}
160
			}
161
		}
162
	},
163
	onMouseDown: function(e){
164
		// summary: event processor for onmousedown
165
		// e: Event: mouse event
166
		if(this._legalMouseDown(e) && (!this.skipForm || !dojo.dnd.isFormElement(e))){
167
			this.mouseDown = true;
168
			this.mouseButton = e.button;
169
			dojo.dnd.Source.superclass.onMouseDown.call(this, e);
170
		}
171
	},
172
	onMouseUp: function(e){
173
		// summary: event processor for onmouseup
174
		// e: Event: mouse event
175
		if(this.mouseDown){
176
			this.mouseDown = false;
177
			dojo.dnd.Source.superclass.onMouseUp.call(this, e);
178
		}
179
	},
180
 
181
	// topic event processors
182
	onDndSourceOver: function(source){
183
		// summary: topic event processor for /dnd/source/over, called when detected a current source
184
		// source: Object: the source which has the mouse over it
185
		if(this != source){
186
			this.mouseDown = false;
187
			if(this.targetAnchor){
188
				this._unmarkTargetAnchor();
189
			}
190
		}else if(this.isDragging){
191
			var m = dojo.dnd.manager();
192
			m.canDrop(this.targetState != "Disabled" && (!this.current || m.source != this || !(this.current.id in this.selection)));
193
		}
194
	},
195
	onDndStart: function(source, nodes, copy){
196
		// summary: topic event processor for /dnd/start, called to initiate the DnD operation
197
		// source: Object: the source which provides items
198
		// nodes: Array: the list of transferred items
199
		// copy: Boolean: copy items, if true, move items otherwise
200
		if(this.isSource){
201
			this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
202
		}
203
		var accepted = this.accept && this.checkAcceptance(source, nodes);
204
		this._changeState("Target", accepted ? "" : "Disabled");
205
		if(accepted && this == source){
206
			dojo.dnd.manager().overSource(this);
207
		}
208
		this.isDragging = true;
209
	},
210
	onDndDrop: function(source, nodes, copy){
211
		// summary: topic event processor for /dnd/drop, called to finish the DnD operation
212
		// source: Object: the source which provides items
213
		// nodes: Array: the list of transferred items
214
		// copy: Boolean: copy items, if true, move items otherwise
215
		do{ //break box
216
			if(this.containerState != "Over"){ break; }
217
			var oldCreator = this._normalizedCreator;
218
			if(this != source){
219
				// transferring nodes from the source to the target
220
				if(this.creator){
221
					// use defined creator
222
					this._normalizedCreator = function(node, hint){
223
						return oldCreator.call(this, source.getItem(node.id).data, hint);
224
					};
225
				}else{
226
					// we have no creator defined => move/clone nodes
227
					if(copy){
228
						// clone nodes
229
						this._normalizedCreator = function(node, hint){
230
							var t = source.getItem(node.id);
231
							var n = node.cloneNode(true);
232
							n.id = dojo.dnd.getUniqueId();
233
							return {node: n, data: t.data, type: t.type};
234
						};
235
					}else{
236
						// move nodes
237
						this._normalizedCreator = function(node, hint){
238
							var t = source.getItem(node.id);
239
							source.delItem(node.id);
240
							return {node: node, data: t.data, type: t.type};
241
						};
242
					}
243
				}
244
			}else{
245
				// transferring nodes within the single source
246
				if(this.current && this.current.id in this.selection){ break; }
247
				if(this.creator){
248
					// use defined creator
249
					if(copy){
250
						// create new copies of data items
251
						this._normalizedCreator = function(node, hint){
252
							return oldCreator.call(this, source.getItem(node.id).data, hint);
253
						};
254
					}else{
255
						// move nodes
256
						if(!this.current){ break; }
257
						this._normalizedCreator = function(node, hint){
258
							var t = source.getItem(node.id);
259
							return {node: node, data: t.data, type: t.type};
260
						};
261
					}
262
				}else{
263
					// we have no creator defined => move/clone nodes
264
					if(copy){
265
						// clone nodes
266
						this._normalizedCreator = function(node, hint){
267
							var t = source.getItem(node.id);
268
							var n = node.cloneNode(true);
269
							n.id = dojo.dnd.getUniqueId();
270
							return {node: n, data: t.data, type: t.type};
271
						};
272
					}else{
273
						// move nodes
274
						if(!this.current){ break; }
275
						this._normalizedCreator = function(node, hint){
276
							var t = source.getItem(node.id);
277
							return {node: node, data: t.data, type: t.type};
278
						};
279
					}
280
				}
281
			}
282
			this._removeSelection();
283
			if(this != source){
284
				this._removeAnchor();
285
			}
286
			if(this != source && !copy && !this.creator){
287
				source.selectNone();
288
			}
289
			this.insertNodes(true, nodes, this.before, this.current);
290
			if(this != source && !copy && this.creator){
291
				source.deleteSelectedNodes();
292
			}
293
			this._normalizedCreator = oldCreator;
294
		}while(false);
295
		this.onDndCancel();
296
	},
297
	onDndCancel: function(){
298
		// summary: topic event processor for /dnd/cancel, called to cancel the DnD operation
299
		if(this.targetAnchor){
300
			this._unmarkTargetAnchor();
301
			this.targetAnchor = null;
302
		}
303
		this.before = true;
304
		this.isDragging = false;
305
		this.mouseDown = false;
306
		delete this.mouseButton;
307
		this._changeState("Source", "");
308
		this._changeState("Target", "");
309
	},
310
 
311
	// utilities
312
	onOverEvent: function(){
313
		// summary: this function is called once, when mouse is over our container
314
		dojo.dnd.Source.superclass.onOverEvent.call(this);
315
		dojo.dnd.manager().overSource(this);
316
	},
317
	onOutEvent: function(){
318
		// summary: this function is called once, when mouse is out of our container
319
		dojo.dnd.Source.superclass.onOutEvent.call(this);
320
		dojo.dnd.manager().outSource(this);
321
	},
322
	_markTargetAnchor: function(before){
323
		// summary: assigns a class to the current target anchor based on "before" status
324
		// before: Boolean: insert before, if true, after otherwise
325
		if(this.current == this.targetAnchor && this.before == before){ return; }
326
		if(this.targetAnchor){
327
			this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
328
		}
329
		this.targetAnchor = this.current;
330
		this.targetBox = null;
331
		this.before = before;
332
		if(this.targetAnchor){
333
			this._addItemClass(this.targetAnchor, this.before ? "Before" : "After");
334
		}
335
	},
336
	_unmarkTargetAnchor: function(){
337
		// summary: removes a class of the current target anchor based on "before" status
338
		if(!this.targetAnchor){ return; }
339
		this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After");
340
		this.targetAnchor = null;
341
		this.targetBox = null;
342
		this.before = true;
343
	},
344
	_markDndStatus: function(copy){
345
		// summary: changes source's state based on "copy" status
346
		this._changeState("Source", copy ? "Copied" : "Moved");
347
	},
348
	_legalMouseDown: function(e){
349
		// summary: checks if user clicked on "approved" items
350
		// e: Event: mouse event
351
		if(!this.withHandles){ return true; }
352
		for(var node = e.target; node && !dojo.hasClass(node, "dojoDndItem"); node = node.parentNode){
353
			if(dojo.hasClass(node, "dojoDndHandle")){ return true; }
354
		}
355
		return false;	// Boolean
356
	}
357
});
358
 
359
dojo.declare("dojo.dnd.Target", dojo.dnd.Source, {
360
	// summary: a Target object, which can be used as a DnD target
361
 
362
	constructor: function(node, params){
363
		// summary: a constructor of the Target --- see the Source constructor for details
364
		this.isSource = false;
365
		dojo.removeClass(this.node, "dojoDndSource");
366
	},
367
 
368
	// markup methods
369
	markupFactory: function(params, node){
370
		params._skipStartup = true;
371
		return new dojo.dnd.Target(node, params);
372
	}
373
});
374
 
375
}