Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2150 mathias 1
if(!dojo._hasResource["dojo.dnd.Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2
dojo._hasResource["dojo.dnd.Container"] = true;
3
dojo.provide("dojo.dnd.Container");
4
 
5
dojo.require("dojo.dnd.common");
6
dojo.require("dojo.parser");
7
 
8
/*
9
	Container states:
10
		""		- normal state
11
		"Over"	- mouse over a container
12
	Container item states:
13
		""		- normal state
14
		"Over"	- mouse over a container item
15
*/
16
 
17
dojo.declare("dojo.dnd.Container", null, {
18
	// summary: a Container object, which knows when mouse hovers over it,
19
	//	and know over which element it hovers
20
 
21
	// object attributes (for markup)
22
	skipForm: false,
23
 
24
	constructor: function(node, params){
25
		// summary: a constructor of the Container
26
		// node: Node: node or node's id to build the container on
27
		// params: Object: a dict of parameters, recognized parameters are:
28
		//	creator: Function: a creator function, which takes a data item, and returns an object like that:
29
		//		{node: newNode, data: usedData, type: arrayOfStrings}
30
		//	skipForm: Boolean: don't start the drag operation, if clicked on form elements
31
		//	_skipStartup: Boolean: skip startup(), which collects children, for deferred initialization
32
		//		(this is used in the markup mode)
33
		this.node = dojo.byId(node);
34
		if(!params){ params = {}; }
35
		this.creator = params.creator || null;
36
		this.skipForm = params.skipForm;
37
		this.defaultCreator = dojo.dnd._defaultCreator(this.node);
38
 
39
		// class-specific variables
40
		this.map = {};
41
		this.current = null;
42
 
43
		// states
44
		this.containerState = "";
45
		dojo.addClass(this.node, "dojoDndContainer");
46
 
47
		// mark up children
48
		if(!(params && params._skipStartup)){
49
			this.startup();
50
		}
51
 
52
		// set up events
53
		this.events = [
54
			dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
55
			dojo.connect(this.node, "onmouseout",  this, "onMouseOut"),
56
			// cancel text selection and text dragging
57
			dojo.connect(this.node, "ondragstart",   this, "onSelectStart"),
58
			dojo.connect(this.node, "onselectstart", this, "onSelectStart")
59
		];
60
	},
61
 
62
	// object attributes (for markup)
63
	creator: function(){},	// creator function, dummy at the moment
64
 
65
	// abstract access to the map
66
	getItem: function(/*String*/ key){
67
		// summary: returns a data item by its key (id)
68
		return this.map[key];	// Object
69
	},
70
	setItem: function(/*String*/ key, /*Object*/ data){
71
		// summary: associates a data item with its key (id)
72
		this.map[key] = data;
73
	},
74
	delItem: function(/*String*/ key){
75
		// summary: removes a data item from the map by its key (id)
76
		delete this.map[key];
77
	},
78
	forInItems: function(/*Function*/ f, /*Object?*/ o){
79
		// summary: iterates over a data map skipping members, which
80
		//	are present in the empty object (IE and/or 3rd-party libraries).
81
		o = o || dojo.global;
82
		var m = this.map, e = dojo.dnd._empty;
83
		for(var i in this.map){
84
			if(i in e){ continue; }
85
			f.call(o, m[i], i, m);
86
		}
87
	},
88
	clearItems: function(){
89
		// summary: removes all data items from the map
90
		this.map = {};
91
	},
92
 
93
	// methods
94
	getAllNodes: function(){
95
		// summary: returns a list (an array) of all valid child nodes
96
		return dojo.query("> .dojoDndItem", this.parent);	// NodeList
97
	},
98
	insertNodes: function(data, before, anchor){
99
		// summary: inserts an array of new nodes before/after an anchor node
100
		// data: Array: a list of data items, which should be processed by the creator function
101
		// before: Boolean: insert before the anchor, if true, and after the anchor otherwise
102
		// anchor: Node: the anchor node to be used as a point of insertion
103
		if(!this.parent.firstChild){
104
			anchor = null;
105
		}else if(before){
106
			if(!anchor){
107
				anchor = this.parent.firstChild;
108
			}
109
		}else{
110
			if(anchor){
111
				anchor = anchor.nextSibling;
112
			}
113
		}
114
		if(anchor){
115
			for(var i = 0; i < data.length; ++i){
116
				var t = this._normalizedCreator(data[i]);
117
				this.setItem(t.node.id, {data: t.data, type: t.type});
118
				this.parent.insertBefore(t.node, anchor);
119
			}
120
		}else{
121
			for(var i = 0; i < data.length; ++i){
122
				var t = this._normalizedCreator(data[i]);
123
				this.setItem(t.node.id, {data: t.data, type: t.type});
124
				this.parent.appendChild(t.node);
125
			}
126
		}
127
		return this;	// self
128
	},
129
	destroy: function(){
130
		// summary: prepares the object to be garbage-collected
131
		dojo.forEach(this.events, dojo.disconnect);
132
		this.clearItems();
133
		this.node = this.parent = this.current;
134
	},
135
 
136
	// markup methods
137
	markupFactory: function(params, node){
138
		params._skipStartup = true;
139
		return new dojo.dnd.Container(node, params);
140
	},
141
	startup: function(){
142
		// summary: collects valid child items and populate the map
143
 
144
		// set up the real parent node
145
		this.parent = this.node;
146
		if(this.parent.tagName.toLowerCase() == "table"){
147
			var c = this.parent.getElementsByTagName("tbody");
148
			if(c && c.length){ this.parent = c[0]; }
149
		}
150
 
151
		// process specially marked children
152
		dojo.query("> .dojoDndItem", this.parent).forEach(function(node){
153
			if(!node.id){ node.id = dojo.dnd.getUniqueId(); }
154
			var type = node.getAttribute("dndType"),
155
				data = node.getAttribute("dndData");
156
			this.setItem(node.id, {
157
				data: data ? data : node.innerHTML,
158
				type: type ? type.split(/\s*,\s*/) : ["text"]
159
			});
160
		}, this);
161
	},
162
 
163
	// mouse events
164
	onMouseOver: function(e){
165
		// summary: event processor for onmouseover
166
		// e: Event: mouse event
167
		var n = e.relatedTarget;
168
		while(n){
169
			if(n == this.node){ break; }
170
			try{
171
				n = n.parentNode;
172
			}catch(x){
173
				n = null;
174
			}
175
		}
176
		if(!n){
177
			this._changeState("Container", "Over");
178
			this.onOverEvent();
179
		}
180
		n = this._getChildByEvent(e);
181
		if(this.current == n){ return; }
182
		if(this.current){ this._removeItemClass(this.current, "Over"); }
183
		if(n){ this._addItemClass(n, "Over"); }
184
		this.current = n;
185
	},
186
	onMouseOut: function(e){
187
		// summary: event processor for onmouseout
188
		// e: Event: mouse event
189
		for(var n = e.relatedTarget; n;){
190
			if(n == this.node){ return; }
191
			try{
192
				n = n.parentNode;
193
			}catch(x){
194
				n = null;
195
			}
196
		}
197
		if(this.current){
198
			this._removeItemClass(this.current, "Over");
199
			this.current = null;
200
		}
201
		this._changeState("Container", "");
202
		this.onOutEvent();
203
	},
204
	onSelectStart: function(e){
205
		// summary: event processor for onselectevent and ondragevent
206
		// e: Event: mouse event
207
		if(!this.skipForm || !dojo.dnd.isFormElement(e)){
208
			dojo.stopEvent(e);
209
		}
210
	},
211
 
212
	// utilities
213
	onOverEvent: function(){
214
		// summary: this function is called once, when mouse is over our container
215
	},
216
	onOutEvent: function(){
217
		// summary: this function is called once, when mouse is out of our container
218
	},
219
	_changeState: function(type, newState){
220
		// summary: changes a named state to new state value
221
		// type: String: a name of the state to change
222
		// newState: String: new state
223
		var prefix = "dojoDnd" + type;
224
		var state  = type.toLowerCase() + "State";
225
		//dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
226
		dojo.removeClass(this.node, prefix + this[state]);
227
		dojo.addClass(this.node, prefix + newState);
228
		this[state] = newState;
229
	},
230
	_addItemClass: function(node, type){
231
		// summary: adds a class with prefix "dojoDndItem"
232
		// node: Node: a node
233
		// type: String: a variable suffix for a class name
234
		dojo.addClass(node, "dojoDndItem" + type);
235
	},
236
	_removeItemClass: function(node, type){
237
		// summary: removes a class with prefix "dojoDndItem"
238
		// node: Node: a node
239
		// type: String: a variable suffix for a class name
240
		dojo.removeClass(node, "dojoDndItem" + type);
241
	},
242
	_getChildByEvent: function(e){
243
		// summary: gets a child, which is under the mouse at the moment, or null
244
		// e: Event: a mouse event
245
		var node = e.target;
246
		if(node){
247
			for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
248
				if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
249
			}
250
		}
251
		return null;
252
	},
253
	_normalizedCreator: function(item, hint){
254
		// summary: adds all necessary data to the output of the user-supplied creator function
255
		var t = (this.creator ? this.creator : this.defaultCreator)(item, hint);
256
		if(!dojo.isArray(t.type)){ t.type = ["text"]; }
257
		if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
258
		dojo.addClass(t.node, "dojoDndItem");
259
		return t;
260
	}
261
});
262
 
263
dojo.dnd._createNode = function(tag){
264
	// summary: returns a function, which creates an element of given tag
265
	//	(SPAN by default) and sets its innerHTML to given text
266
	// tag: String: a tag name or empty for SPAN
267
	if(!tag){ return dojo.dnd._createSpan; }
268
	return function(text){	// Function
269
		var n = dojo.doc.createElement(tag);
270
		n.innerHTML = text;
271
		return n;
272
	};
273
};
274
 
275
dojo.dnd._createTrTd = function(text){
276
	// summary: creates a TR/TD structure with given text as an innerHTML of TD
277
	// text: String: a text for TD
278
	var tr = dojo.doc.createElement("tr");
279
	var td = dojo.doc.createElement("td");
280
	td.innerHTML = text;
281
	tr.appendChild(td);
282
	return tr;	// Node
283
};
284
 
285
dojo.dnd._createSpan = function(text){
286
	// summary: creates a SPAN element with given text as its innerHTML
287
	// text: String: a text for SPAN
288
	var n = dojo.doc.createElement("span");
289
	n.innerHTML = text;
290
	return n;	// Node
291
};
292
 
293
// dojo.dnd._defaultCreatorNodes: Object: a dicitionary, which maps container tag names to child tag names
294
dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
295
 
296
dojo.dnd._defaultCreator = function(node){
297
	// summary: takes a container node, and returns an appropriate creator function
298
	// node: Node: a container node
299
	var tag = node.tagName.toLowerCase();
300
	var c = tag == "table" ? dojo.dnd._createTrTd : dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
301
	return function(item, hint){	// Function
302
		var isObj = dojo.isObject(item) && item;
303
		var data = (isObj && item.data) ? item.data : item;
304
		var type = (isObj && item.type) ? item.type : ["text"];
305
		var t = String(data), n = (hint == "avatar" ? dojo.dnd._createSpan : c)(t);
306
		n.id = dojo.dnd.getUniqueId();
307
		return {node: n, data: data, type: type};
308
	};
309
};
310
 
311
}