]> git.wh0rd.org - tt-rss.git/blob - lib/dojo/dnd/Container.js.uncompressed.js
update dojo to 1.7.3
[tt-rss.git] / lib / dojo / dnd / Container.js.uncompressed.js
1 define("dojo/dnd/Container", ["../main", "../Evented", "./common", "../parser"], function(dojo, Evented) {
2 // module:
3 // dojo/dnd/Container
4 // summary:
5 // TODOC
6
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 /*=====
18 dojo.declare("dojo.dnd.__ContainerArgs", [], {
19 creator: function(){
20 // summary:
21 // a creator function, which takes a data item, and returns an object like that:
22 // {node: newNode, data: usedData, type: arrayOfStrings}
23 },
24
25 // skipForm: Boolean
26 // don't start the drag operation, if clicked on form elements
27 skipForm: false,
28
29 // dropParent: Node||String
30 // node or node's id to use as the parent node for dropped items
31 // (must be underneath the 'node' parameter in the DOM)
32 dropParent: null,
33
34 // _skipStartup: Boolean
35 // skip startup(), which collects children, for deferred initialization
36 // (this is used in the markup mode)
37 _skipStartup: false
38 });
39
40 dojo.dnd.Item = function(){
41 // summary:
42 // Represents (one of) the source node(s) being dragged.
43 // Contains (at least) the "type" and "data" attributes.
44 // type: String[]
45 // Type(s) of this item, by default this is ["text"]
46 // data: Object
47 // Logical representation of the object being dragged.
48 // If the drag object's type is "text" then data is a String,
49 // if it's another type then data could be a different Object,
50 // perhaps a name/value hash.
51
52 this.type = type;
53 this.data = data;
54 }
55 =====*/
56
57 dojo.declare("dojo.dnd.Container", Evented, {
58 // summary:
59 // a Container object, which knows when mouse hovers over it,
60 // and over which element it hovers
61
62 // object attributes (for markup)
63 skipForm: false,
64
65 /*=====
66 // current: DomNode
67 // The DOM node the mouse is currently hovered over
68 current: null,
69
70 // map: Hash<String, dojo.dnd.Item>
71 // Map from an item's id (which is also the DOMNode's id) to
72 // the dojo.dnd.Item itself.
73 map: {},
74 =====*/
75
76 constructor: function(node, params){
77 // summary:
78 // a constructor of the Container
79 // node: Node
80 // node or node's id to build the container on
81 // params: dojo.dnd.__ContainerArgs
82 // a dictionary of parameters
83 this.node = dojo.byId(node);
84 if(!params){ params = {}; }
85 this.creator = params.creator || null;
86 this.skipForm = params.skipForm;
87 this.parent = params.dropParent && dojo.byId(params.dropParent);
88
89 // class-specific variables
90 this.map = {};
91 this.current = null;
92
93 // states
94 this.containerState = "";
95 dojo.addClass(this.node, "dojoDndContainer");
96
97 // mark up children
98 if(!(params && params._skipStartup)){
99 this.startup();
100 }
101
102 // set up events
103 this.events = [
104 dojo.connect(this.node, "onmouseover", this, "onMouseOver"),
105 dojo.connect(this.node, "onmouseout", this, "onMouseOut"),
106 // cancel text selection and text dragging
107 dojo.connect(this.node, "ondragstart", this, "onSelectStart"),
108 dojo.connect(this.node, "onselectstart", this, "onSelectStart")
109 ];
110 },
111
112 // object attributes (for markup)
113 creator: function(){
114 // summary:
115 // creator function, dummy at the moment
116 },
117
118 // abstract access to the map
119 getItem: function(/*String*/ key){
120 // summary:
121 // returns a data item by its key (id)
122 return this.map[key]; // dojo.dnd.Item
123 },
124 setItem: function(/*String*/ key, /*dojo.dnd.Item*/ data){
125 // summary:
126 // associates a data item with its key (id)
127 this.map[key] = data;
128 },
129 delItem: function(/*String*/ key){
130 // summary:
131 // removes a data item from the map by its key (id)
132 delete this.map[key];
133 },
134 forInItems: function(/*Function*/ f, /*Object?*/ o){
135 // summary:
136 // iterates over a data map skipping members that
137 // are present in the empty object (IE and/or 3rd-party libraries).
138 o = o || dojo.global;
139 var m = this.map, e = dojo.dnd._empty;
140 for(var i in m){
141 if(i in e){ continue; }
142 f.call(o, m[i], i, this);
143 }
144 return o; // Object
145 },
146 clearItems: function(){
147 // summary:
148 // removes all data items from the map
149 this.map = {};
150 },
151
152 // methods
153 getAllNodes: function(){
154 // summary:
155 // returns a list (an array) of all valid child nodes
156 return dojo.query("> .dojoDndItem", this.parent); // NodeList
157 },
158 sync: function(){
159 // summary:
160 // sync up the node list with the data map
161 var map = {};
162 this.getAllNodes().forEach(function(node){
163 if(node.id){
164 var item = this.getItem(node.id);
165 if(item){
166 map[node.id] = item;
167 return;
168 }
169 }else{
170 node.id = dojo.dnd.getUniqueId();
171 }
172 var type = node.getAttribute("dndType"),
173 data = node.getAttribute("dndData");
174 map[node.id] = {
175 data: data || node.innerHTML,
176 type: type ? type.split(/\s*,\s*/) : ["text"]
177 };
178 }, this);
179 this.map = map;
180 return this; // self
181 },
182 insertNodes: function(data, before, anchor){
183 // summary:
184 // inserts an array of new nodes before/after an anchor node
185 // data: Array
186 // a list of data items, which should be processed by the creator function
187 // before: Boolean
188 // insert before the anchor, if true, and after the anchor otherwise
189 // anchor: Node
190 // the anchor node to be used as a point of insertion
191 if(!this.parent.firstChild){
192 anchor = null;
193 }else if(before){
194 if(!anchor){
195 anchor = this.parent.firstChild;
196 }
197 }else{
198 if(anchor){
199 anchor = anchor.nextSibling;
200 }
201 }
202 if(anchor){
203 for(var i = 0; i < data.length; ++i){
204 var t = this._normalizedCreator(data[i]);
205 this.setItem(t.node.id, {data: t.data, type: t.type});
206 this.parent.insertBefore(t.node, anchor);
207 }
208 }else{
209 for(var i = 0; i < data.length; ++i){
210 var t = this._normalizedCreator(data[i]);
211 this.setItem(t.node.id, {data: t.data, type: t.type});
212 this.parent.appendChild(t.node);
213 }
214 }
215 return this; // self
216 },
217 destroy: function(){
218 // summary:
219 // prepares this object to be garbage-collected
220 dojo.forEach(this.events, dojo.disconnect);
221 this.clearItems();
222 this.node = this.parent = this.current = null;
223 },
224
225 // markup methods
226 markupFactory: function(params, node, ctor){
227 params._skipStartup = true;
228 return new ctor(node, params);
229 },
230 startup: function(){
231 // summary:
232 // collects valid child items and populate the map
233
234 // set up the real parent node
235 if(!this.parent){
236 // use the standard algorithm, if not assigned
237 this.parent = this.node;
238 if(this.parent.tagName.toLowerCase() == "table"){
239 var c = this.parent.getElementsByTagName("tbody");
240 if(c && c.length){ this.parent = c[0]; }
241 }
242 }
243 this.defaultCreator = dojo.dnd._defaultCreator(this.parent);
244
245 // process specially marked children
246 this.sync();
247 },
248
249 // mouse events
250 onMouseOver: function(e){
251 // summary:
252 // event processor for onmouseover
253 // e: Event
254 // mouse event
255 var n = e.relatedTarget;
256 while(n){
257 if(n == this.node){ break; }
258 try{
259 n = n.parentNode;
260 }catch(x){
261 n = null;
262 }
263 }
264 if(!n){
265 this._changeState("Container", "Over");
266 this.onOverEvent();
267 }
268 n = this._getChildByEvent(e);
269 if(this.current == n){ return; }
270 if(this.current){ this._removeItemClass(this.current, "Over"); }
271 if(n){ this._addItemClass(n, "Over"); }
272 this.current = n;
273 },
274 onMouseOut: function(e){
275 // summary:
276 // event processor for onmouseout
277 // e: Event
278 // mouse event
279 for(var n = e.relatedTarget; n;){
280 if(n == this.node){ return; }
281 try{
282 n = n.parentNode;
283 }catch(x){
284 n = null;
285 }
286 }
287 if(this.current){
288 this._removeItemClass(this.current, "Over");
289 this.current = null;
290 }
291 this._changeState("Container", "");
292 this.onOutEvent();
293 },
294 onSelectStart: function(e){
295 // summary:
296 // event processor for onselectevent and ondragevent
297 // e: Event
298 // mouse event
299 if(!this.skipForm || !dojo.dnd.isFormElement(e)){
300 dojo.stopEvent(e);
301 }
302 },
303
304 // utilities
305 onOverEvent: function(){
306 // summary:
307 // this function is called once, when mouse is over our container
308 },
309 onOutEvent: function(){
310 // summary:
311 // this function is called once, when mouse is out of our container
312 },
313 _changeState: function(type, newState){
314 // summary:
315 // changes a named state to new state value
316 // type: String
317 // a name of the state to change
318 // newState: String
319 // new state
320 var prefix = "dojoDnd" + type;
321 var state = type.toLowerCase() + "State";
322 //dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
323 dojo.replaceClass(this.node, prefix + newState, prefix + this[state]);
324 this[state] = newState;
325 },
326 _addItemClass: function(node, type){
327 // summary:
328 // adds a class with prefix "dojoDndItem"
329 // node: Node
330 // a node
331 // type: String
332 // a variable suffix for a class name
333 dojo.addClass(node, "dojoDndItem" + type);
334 },
335 _removeItemClass: function(node, type){
336 // summary:
337 // removes a class with prefix "dojoDndItem"
338 // node: Node
339 // a node
340 // type: String
341 // a variable suffix for a class name
342 dojo.removeClass(node, "dojoDndItem" + type);
343 },
344 _getChildByEvent: function(e){
345 // summary:
346 // gets a child, which is under the mouse at the moment, or null
347 // e: Event
348 // a mouse event
349 var node = e.target;
350 if(node){
351 for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
352 if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
353 }
354 }
355 return null;
356 },
357 _normalizedCreator: function(/*dojo.dnd.Item*/ item, /*String*/ hint){
358 // summary:
359 // adds all necessary data to the output of the user-supplied creator function
360 var t = (this.creator || this.defaultCreator).call(this, item, hint);
361 if(!dojo.isArray(t.type)){ t.type = ["text"]; }
362 if(!t.node.id){ t.node.id = dojo.dnd.getUniqueId(); }
363 dojo.addClass(t.node, "dojoDndItem");
364 return t;
365 }
366 });
367
368 dojo.dnd._createNode = function(tag){
369 // summary:
370 // returns a function, which creates an element of given tag
371 // (SPAN by default) and sets its innerHTML to given text
372 // tag: String
373 // a tag name or empty for SPAN
374 if(!tag){ return dojo.dnd._createSpan; }
375 return function(text){ // Function
376 return dojo.create(tag, {innerHTML: text}); // Node
377 };
378 };
379
380 dojo.dnd._createTrTd = function(text){
381 // summary:
382 // creates a TR/TD structure with given text as an innerHTML of TD
383 // text: String
384 // a text for TD
385 var tr = dojo.create("tr");
386 dojo.create("td", {innerHTML: text}, tr);
387 return tr; // Node
388 };
389
390 dojo.dnd._createSpan = function(text){
391 // summary:
392 // creates a SPAN element with given text as its innerHTML
393 // text: String
394 // a text for SPAN
395 return dojo.create("span", {innerHTML: text}); // Node
396 };
397
398 // dojo.dnd._defaultCreatorNodes: Object
399 // a dictionary that maps container tag names to child tag names
400 dojo.dnd._defaultCreatorNodes = {ul: "li", ol: "li", div: "div", p: "div"};
401
402 dojo.dnd._defaultCreator = function(node){
403 // summary:
404 // takes a parent node, and returns an appropriate creator function
405 // node: Node
406 // a container node
407 var tag = node.tagName.toLowerCase();
408 var c = tag == "tbody" || tag == "thead" ? dojo.dnd._createTrTd :
409 dojo.dnd._createNode(dojo.dnd._defaultCreatorNodes[tag]);
410 return function(item, hint){ // Function
411 var isObj = item && dojo.isObject(item), data, type, n;
412 if(isObj && item.tagName && item.nodeType && item.getAttribute){
413 // process a DOM node
414 data = item.getAttribute("dndData") || item.innerHTML;
415 type = item.getAttribute("dndType");
416 type = type ? type.split(/\s*,\s*/) : ["text"];
417 n = item; // this node is going to be moved rather than copied
418 }else{
419 // process a DnD item object or a string
420 data = (isObj && item.data) ? item.data : item;
421 type = (isObj && item.type) ? item.type : ["text"];
422 n = (hint == "avatar" ? dojo.dnd._createSpan : c)(String(data));
423 }
424 if(!n.id){
425 n.id = dojo.dnd.getUniqueId();
426 }
427 return {node: n, data: data, type: type};
428 };
429 };
430
431 return dojo.dnd.Container;
432 });