]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojo/dnd/Container.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dojo / dnd / Container.js
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                 this.getAllNodes().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 }