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");
5 dojo.require("dojo.dnd.common");
6 dojo.require("dojo.parser");
11 "Over" - mouse over a container
12 Container item states:
14 "Over" - mouse over a container item
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
21 // object attributes (for markup)
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);
39 // class-specific variables
44 this.containerState = "";
45 dojo.addClass(this.node, "dojoDndContainer");
48 if(!(params && params._skipStartup)){
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")
62 // object attributes (for markup)
63 creator: function(){}, // creator function, dummy at the moment
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
70 setItem: function(/*String*/ key, /*Object*/ data){
71 // summary: associates a data item with its key (id)
74 delItem: function(/*String*/ key){
75 // summary: removes a data item from the map by its key (id)
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).
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);
88 clearItems: function(){
89 // summary: removes all data items from the map
94 getAllNodes: function(){
95 // summary: returns a list (an array) of all valid child nodes
96 return dojo.query("> .dojoDndItem", this.parent); // NodeList
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){
107 anchor = this.parent.firstChild;
111 anchor = anchor.nextSibling;
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);
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);
130 // summary: prepares the object to be garbage-collected
131 dojo.forEach(this.events, dojo.disconnect);
133 this.node = this.parent = this.current;
137 markupFactory: function(params, node){
138 params._skipStartup = true;
139 return new dojo.dnd.Container(node, params);
142 // summary: collects valid child items and populate the map
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]; }
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"]
164 onMouseOver: function(e){
165 // summary: event processor for onmouseover
166 // e: Event: mouse event
167 var n = e.relatedTarget;
169 if(n == this.node){ break; }
177 this._changeState("Container", "Over");
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"); }
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; }
198 this._removeItemClass(this.current, "Over");
201 this._changeState("Container", "");
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)){
213 onOverEvent: function(){
214 // summary: this function is called once, when mouse is over our container
216 onOutEvent: function(){
217 // summary: this function is called once, when mouse is out of our container
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;
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);
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);
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
247 for(var parent = node.parentNode; parent; node = parent, parent = node.parentNode){
248 if(parent == this.parent && dojo.hasClass(node, "dojoDndItem")){ return node; }
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");
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);
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");
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");
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"};
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};