]> git.wh0rd.org Git - 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 });