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 |
}
|