]>
Commit | Line | Data |
---|---|---|
1354d172 AD |
1 | define("dojo/dnd/Selector", ["../main", "./common", "./Container"], function(dojo) { |
2 | // module: | |
3 | // dojo/dnd/Selector | |
4 | // summary: | |
5 | // TODOC | |
6 | ||
7 | ||
8 | /* | |
9 | Container item states: | |
10 | "" - an item is not selected | |
11 | "Selected" - an item is selected | |
12 | "Anchor" - an item is selected, and is an anchor for a "shift" selection | |
13 | */ | |
14 | ||
15 | /*===== | |
16 | dojo.declare("dojo.dnd.__SelectorArgs", [dojo.dnd.__ContainerArgs], { | |
17 | // singular: Boolean | |
18 | // allows selection of only one element, if true | |
19 | singular: false, | |
20 | ||
21 | // autoSync: Boolean | |
22 | // autosynchronizes the source with its list of DnD nodes, | |
23 | autoSync: false | |
24 | }); | |
25 | =====*/ | |
26 | ||
27 | dojo.declare("dojo.dnd.Selector", dojo.dnd.Container, { | |
28 | // summary: | |
29 | // a Selector object, which knows how to select its children | |
30 | ||
31 | /*===== | |
32 | // selection: Set<String> | |
33 | // The set of id's that are currently selected, such that this.selection[id] == 1 | |
34 | // if the node w/that id is selected. Can iterate over selected node's id's like: | |
35 | // | for(var id in this.selection) | |
36 | selection: {}, | |
37 | =====*/ | |
38 | ||
39 | constructor: function(node, params){ | |
40 | // summary: | |
41 | // constructor of the Selector | |
42 | // node: Node||String | |
43 | // node or node's id to build the selector on | |
44 | // params: dojo.dnd.__SelectorArgs? | |
45 | // a dictionary of parameters | |
46 | if(!params){ params = {}; } | |
47 | this.singular = params.singular; | |
48 | this.autoSync = params.autoSync; | |
49 | // class-specific variables | |
50 | this.selection = {}; | |
51 | this.anchor = null; | |
52 | this.simpleSelection = false; | |
53 | // set up events | |
54 | this.events.push( | |
55 | dojo.connect(this.node, "onmousedown", this, "onMouseDown"), | |
56 | dojo.connect(this.node, "onmouseup", this, "onMouseUp")); | |
57 | }, | |
58 | ||
59 | // object attributes (for markup) | |
60 | singular: false, // is singular property | |
61 | ||
62 | // methods | |
63 | getSelectedNodes: function(){ | |
64 | // summary: | |
65 | // returns a list (an array) of selected nodes | |
66 | var t = new dojo.NodeList(); | |
67 | var e = dojo.dnd._empty; | |
68 | for(var i in this.selection){ | |
69 | if(i in e){ continue; } | |
70 | t.push(dojo.byId(i)); | |
71 | } | |
72 | return t; // NodeList | |
73 | }, | |
74 | selectNone: function(){ | |
75 | // summary: | |
76 | // unselects all items | |
77 | return this._removeSelection()._removeAnchor(); // self | |
78 | }, | |
79 | selectAll: function(){ | |
80 | // summary: | |
81 | // selects all items | |
82 | this.forInItems(function(data, id){ | |
83 | this._addItemClass(dojo.byId(id), "Selected"); | |
84 | this.selection[id] = 1; | |
85 | }, this); | |
86 | return this._removeAnchor(); // self | |
87 | }, | |
88 | deleteSelectedNodes: function(){ | |
89 | // summary: | |
90 | // deletes all selected items | |
91 | var e = dojo.dnd._empty; | |
92 | for(var i in this.selection){ | |
93 | if(i in e){ continue; } | |
94 | var n = dojo.byId(i); | |
95 | this.delItem(i); | |
96 | dojo.destroy(n); | |
97 | } | |
98 | this.anchor = null; | |
99 | this.selection = {}; | |
100 | return this; // self | |
101 | }, | |
102 | forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){ | |
103 | // summary: | |
104 | // iterates over selected items; | |
105 | // see `dojo.dnd.Container.forInItems()` for details | |
106 | o = o || dojo.global; | |
107 | var s = this.selection, e = dojo.dnd._empty; | |
108 | for(var i in s){ | |
109 | if(i in e){ continue; } | |
110 | f.call(o, this.getItem(i), i, this); | |
111 | } | |
112 | }, | |
113 | sync: function(){ | |
114 | // summary: | |
115 | // sync up the node list with the data map | |
116 | ||
117 | dojo.dnd.Selector.superclass.sync.call(this); | |
118 | ||
119 | // fix the anchor | |
120 | if(this.anchor){ | |
121 | if(!this.getItem(this.anchor.id)){ | |
122 | this.anchor = null; | |
123 | } | |
124 | } | |
125 | ||
126 | // fix the selection | |
127 | var t = [], e = dojo.dnd._empty; | |
128 | for(var i in this.selection){ | |
129 | if(i in e){ continue; } | |
130 | if(!this.getItem(i)){ | |
131 | t.push(i); | |
132 | } | |
133 | } | |
134 | dojo.forEach(t, function(i){ | |
135 | delete this.selection[i]; | |
136 | }, this); | |
137 | ||
138 | return this; // self | |
139 | }, | |
140 | insertNodes: function(addSelected, data, before, anchor){ | |
141 | // summary: | |
142 | // inserts new data items (see `dojo.dnd.Container.insertNodes()` method for details) | |
143 | // addSelected: Boolean | |
144 | // all new nodes will be added to selected items, if true, no selection change otherwise | |
145 | // data: Array | |
146 | // a list of data items, which should be processed by the creator function | |
147 | // before: Boolean | |
148 | // insert before the anchor, if true, and after the anchor otherwise | |
149 | // anchor: Node | |
150 | // the anchor node to be used as a point of insertion | |
151 | var oldCreator = this._normalizedCreator; | |
152 | this._normalizedCreator = function(item, hint){ | |
153 | var t = oldCreator.call(this, item, hint); | |
154 | if(addSelected){ | |
155 | if(!this.anchor){ | |
156 | this.anchor = t.node; | |
157 | this._removeItemClass(t.node, "Selected"); | |
158 | this._addItemClass(this.anchor, "Anchor"); | |
159 | }else if(this.anchor != t.node){ | |
160 | this._removeItemClass(t.node, "Anchor"); | |
161 | this._addItemClass(t.node, "Selected"); | |
162 | } | |
163 | this.selection[t.node.id] = 1; | |
164 | }else{ | |
165 | this._removeItemClass(t.node, "Selected"); | |
166 | this._removeItemClass(t.node, "Anchor"); | |
167 | } | |
168 | return t; | |
169 | }; | |
170 | dojo.dnd.Selector.superclass.insertNodes.call(this, data, before, anchor); | |
171 | this._normalizedCreator = oldCreator; | |
172 | return this; // self | |
173 | }, | |
174 | destroy: function(){ | |
175 | // summary: | |
176 | // prepares the object to be garbage-collected | |
177 | dojo.dnd.Selector.superclass.destroy.call(this); | |
178 | this.selection = this.anchor = null; | |
179 | }, | |
180 | ||
181 | // mouse events | |
182 | onMouseDown: function(e){ | |
183 | // summary: | |
184 | // event processor for onmousedown | |
185 | // e: Event | |
186 | // mouse event | |
187 | if(this.autoSync){ this.sync(); } | |
188 | if(!this.current){ return; } | |
189 | if(!this.singular && !dojo.isCopyKey(e) && !e.shiftKey && (this.current.id in this.selection)){ | |
190 | this.simpleSelection = true; | |
191 | if(e.button === dojo.mouseButtons.LEFT){ | |
192 | // accept the left button and stop the event | |
193 | // for IE we don't stop event when multiple buttons are pressed | |
194 | dojo.stopEvent(e); | |
195 | } | |
196 | return; | |
197 | } | |
198 | if(!this.singular && e.shiftKey){ | |
199 | if(!dojo.isCopyKey(e)){ | |
200 | this._removeSelection(); | |
201 | } | |
202 | var c = this.getAllNodes(); | |
203 | if(c.length){ | |
204 | if(!this.anchor){ | |
205 | this.anchor = c[0]; | |
206 | this._addItemClass(this.anchor, "Anchor"); | |
207 | } | |
208 | this.selection[this.anchor.id] = 1; | |
209 | if(this.anchor != this.current){ | |
210 | var i = 0; | |
211 | for(; i < c.length; ++i){ | |
212 | var node = c[i]; | |
213 | if(node == this.anchor || node == this.current){ break; } | |
214 | } | |
215 | for(++i; i < c.length; ++i){ | |
216 | var node = c[i]; | |
217 | if(node == this.anchor || node == this.current){ break; } | |
218 | this._addItemClass(node, "Selected"); | |
219 | this.selection[node.id] = 1; | |
220 | } | |
221 | this._addItemClass(this.current, "Selected"); | |
222 | this.selection[this.current.id] = 1; | |
223 | } | |
224 | } | |
225 | }else{ | |
226 | if(this.singular){ | |
227 | if(this.anchor == this.current){ | |
228 | if(dojo.isCopyKey(e)){ | |
229 | this.selectNone(); | |
230 | } | |
231 | }else{ | |
232 | this.selectNone(); | |
233 | this.anchor = this.current; | |
234 | this._addItemClass(this.anchor, "Anchor"); | |
235 | this.selection[this.current.id] = 1; | |
236 | } | |
237 | }else{ | |
238 | if(dojo.isCopyKey(e)){ | |
239 | if(this.anchor == this.current){ | |
240 | delete this.selection[this.anchor.id]; | |
241 | this._removeAnchor(); | |
242 | }else{ | |
243 | if(this.current.id in this.selection){ | |
244 | this._removeItemClass(this.current, "Selected"); | |
245 | delete this.selection[this.current.id]; | |
246 | }else{ | |
247 | if(this.anchor){ | |
248 | this._removeItemClass(this.anchor, "Anchor"); | |
249 | this._addItemClass(this.anchor, "Selected"); | |
250 | } | |
251 | this.anchor = this.current; | |
252 | this._addItemClass(this.current, "Anchor"); | |
253 | this.selection[this.current.id] = 1; | |
254 | } | |
255 | } | |
256 | }else{ | |
257 | if(!(this.current.id in this.selection)){ | |
258 | this.selectNone(); | |
259 | this.anchor = this.current; | |
260 | this._addItemClass(this.current, "Anchor"); | |
261 | this.selection[this.current.id] = 1; | |
262 | } | |
263 | } | |
264 | } | |
265 | } | |
266 | dojo.stopEvent(e); | |
267 | }, | |
268 | onMouseUp: function(e){ | |
269 | // summary: | |
270 | // event processor for onmouseup | |
271 | // e: Event | |
272 | // mouse event | |
273 | if(!this.simpleSelection){ return; } | |
274 | this.simpleSelection = false; | |
275 | this.selectNone(); | |
276 | if(this.current){ | |
277 | this.anchor = this.current; | |
278 | this._addItemClass(this.anchor, "Anchor"); | |
279 | this.selection[this.current.id] = 1; | |
280 | } | |
281 | }, | |
282 | onMouseMove: function(e){ | |
283 | // summary: | |
284 | // event processor for onmousemove | |
285 | // e: Event | |
286 | // mouse event | |
287 | this.simpleSelection = false; | |
288 | }, | |
289 | ||
290 | // utilities | |
291 | onOverEvent: function(){ | |
292 | // summary: | |
293 | // this function is called once, when mouse is over our container | |
294 | this.onmousemoveEvent = dojo.connect(this.node, "onmousemove", this, "onMouseMove"); | |
295 | }, | |
296 | onOutEvent: function(){ | |
297 | // summary: | |
298 | // this function is called once, when mouse is out of our container | |
299 | dojo.disconnect(this.onmousemoveEvent); | |
300 | delete this.onmousemoveEvent; | |
301 | }, | |
302 | _removeSelection: function(){ | |
303 | // summary: | |
304 | // unselects all items | |
305 | var e = dojo.dnd._empty; | |
306 | for(var i in this.selection){ | |
307 | if(i in e){ continue; } | |
308 | var node = dojo.byId(i); | |
309 | if(node){ this._removeItemClass(node, "Selected"); } | |
310 | } | |
311 | this.selection = {}; | |
312 | return this; // self | |
313 | }, | |
314 | _removeAnchor: function(){ | |
315 | if(this.anchor){ | |
316 | this._removeItemClass(this.anchor, "Anchor"); | |
317 | this.anchor = null; | |
318 | } | |
319 | return this; // self | |
320 | } | |
321 | }); | |
322 | ||
323 | return dojo.dnd.Selector; | |
324 | }); |