1 | /*! |
---|
2 | * Ext JS Library 3.0.0 |
---|
3 | * Copyright(c) 2006-2009 Ext JS, LLC |
---|
4 | * licensing@extjs.com |
---|
5 | * http://www.extjs.com/license |
---|
6 | */ |
---|
7 | Ext.ns('Ext.ux.form'); |
---|
8 | |
---|
9 | /** |
---|
10 | * @class Ext.ux.form.MultiSelect |
---|
11 | * @extends Ext.form.Field |
---|
12 | * A control that allows selection and form submission of multiple list items. |
---|
13 | * |
---|
14 | * @history |
---|
15 | * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams) |
---|
16 | * 2008-06-19 bpm Docs and demo code clean up |
---|
17 | * |
---|
18 | * @constructor |
---|
19 | * Create a new MultiSelect |
---|
20 | * @param {Object} config Configuration options |
---|
21 | * @xtype multiselect |
---|
22 | */ |
---|
23 | Ext.ux.form.MultiSelect = Ext.extend(Ext.form.Field, { |
---|
24 | /** |
---|
25 | * @cfg {String} legend Wraps the object with a fieldset and specified legend. |
---|
26 | */ |
---|
27 | /** |
---|
28 | * @cfg {Ext.ListView} view The {@link Ext.ListView} used to render the multiselect list. |
---|
29 | */ |
---|
30 | /** |
---|
31 | * @cfg {String/Array} dragGroup The ddgroup name(s) for the MultiSelect DragZone (defaults to undefined). |
---|
32 | */ |
---|
33 | /** |
---|
34 | * @cfg {String/Array} dropGroup The ddgroup name(s) for the MultiSelect DropZone (defaults to undefined). |
---|
35 | */ |
---|
36 | /** |
---|
37 | * @cfg {Boolean} ddReorder Whether the items in the MultiSelect list are drag/drop reorderable (defaults to false). |
---|
38 | */ |
---|
39 | ddReorder:false, |
---|
40 | /** |
---|
41 | * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a |
---|
42 | * toolbar config, or an array of buttons/button configs to be added to the toolbar. |
---|
43 | */ |
---|
44 | /** |
---|
45 | * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled |
---|
46 | * (use for lists which are sorted, defaults to false). |
---|
47 | */ |
---|
48 | appendOnly:false, |
---|
49 | /** |
---|
50 | * @cfg {Number} width Width in pixels of the control (defaults to 100). |
---|
51 | */ |
---|
52 | width:100, |
---|
53 | /** |
---|
54 | * @cfg {Number} height Height in pixels of the control (defaults to 100). |
---|
55 | */ |
---|
56 | height:100, |
---|
57 | /** |
---|
58 | * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0). |
---|
59 | */ |
---|
60 | displayField:0, |
---|
61 | /** |
---|
62 | * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1). |
---|
63 | */ |
---|
64 | valueField:1, |
---|
65 | /** |
---|
66 | * @cfg {Boolean} allowBlank False to require at least one item in the list to be selected, true to allow no |
---|
67 | * selection (defaults to true). |
---|
68 | */ |
---|
69 | allowBlank:true, |
---|
70 | /** |
---|
71 | * @cfg {Number} minSelections Minimum number of selections allowed (defaults to 0). |
---|
72 | */ |
---|
73 | minSelections:0, |
---|
74 | /** |
---|
75 | * @cfg {Number} maxSelections Maximum number of selections allowed (defaults to Number.MAX_VALUE). |
---|
76 | */ |
---|
77 | maxSelections:Number.MAX_VALUE, |
---|
78 | /** |
---|
79 | * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as |
---|
80 | * {@link Ext.form.TextField#blankText}. |
---|
81 | */ |
---|
82 | blankText:Ext.form.TextField.prototype.blankText, |
---|
83 | /** |
---|
84 | * @cfg {String} minSelectionsText Validation message displayed when {@link #minSelections} is not met (defaults to 'Minimum {0} |
---|
85 | * item(s) required'). The {0} token will be replaced by the value of {@link #minSelections}. |
---|
86 | */ |
---|
87 | minSelectionsText:'Minimum {0} item(s) required', |
---|
88 | /** |
---|
89 | * @cfg {String} maxSelectionsText Validation message displayed when {@link #maxSelections} is not met (defaults to 'Maximum {0} |
---|
90 | * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxSelections}. |
---|
91 | */ |
---|
92 | maxSelectionsText:'Maximum {0} item(s) allowed', |
---|
93 | /** |
---|
94 | * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values |
---|
95 | * (defaults to ','). |
---|
96 | */ |
---|
97 | delimiter:',', |
---|
98 | /** |
---|
99 | * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to <tt>undefined</tt>). |
---|
100 | * Acceptable values for this property are: |
---|
101 | * <div class="mdetail-params"><ul> |
---|
102 | * <li><b>any {@link Ext.data.Store Store} subclass</b></li> |
---|
103 | * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally. |
---|
104 | * <div class="mdetail-params"><ul> |
---|
105 | * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc"> |
---|
106 | * A 1-dimensional array will automatically be expanded (each array item will be the combo |
---|
107 | * {@link #valueField value} and {@link #displayField text})</div></li> |
---|
108 | * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc"> |
---|
109 | * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo |
---|
110 | * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}. |
---|
111 | * </div></li></ul></div></li></ul></div> |
---|
112 | */ |
---|
113 | |
---|
114 | // private |
---|
115 | defaultAutoCreate : {tag: "div"}, |
---|
116 | |
---|
117 | // private |
---|
118 | initComponent: function(){ |
---|
119 | Ext.ux.form.MultiSelect.superclass.initComponent.call(this); |
---|
120 | |
---|
121 | if(Ext.isArray(this.store)){ |
---|
122 | if (Ext.isArray(this.store[0])){ |
---|
123 | this.store = new Ext.data.ArrayStore({ |
---|
124 | fields: ['value','text'], |
---|
125 | data: this.store |
---|
126 | }); |
---|
127 | this.valueField = 'value'; |
---|
128 | }else{ |
---|
129 | this.store = new Ext.data.ArrayStore({ |
---|
130 | fields: ['text'], |
---|
131 | data: this.store, |
---|
132 | expandData: true |
---|
133 | }); |
---|
134 | this.valueField = 'text'; |
---|
135 | } |
---|
136 | this.displayField = 'text'; |
---|
137 | } else { |
---|
138 | this.store = Ext.StoreMgr.lookup(this.store); |
---|
139 | } |
---|
140 | |
---|
141 | this.addEvents({ |
---|
142 | 'dblclick' : true, |
---|
143 | 'click' : true, |
---|
144 | 'change' : true, |
---|
145 | 'drop' : true |
---|
146 | }); |
---|
147 | }, |
---|
148 | |
---|
149 | // private |
---|
150 | onRender: function(ct, position){ |
---|
151 | Ext.ux.form.MultiSelect.superclass.onRender.call(this, ct, position); |
---|
152 | |
---|
153 | var fs = this.fs = new Ext.form.FieldSet({ |
---|
154 | renderTo: this.el, |
---|
155 | title: this.legend, |
---|
156 | height: this.height, |
---|
157 | width: this.width, |
---|
158 | style: "padding:0;", |
---|
159 | tbar: this.tbar, |
---|
160 | bodyStyle: 'overflow: auto;' |
---|
161 | }); |
---|
162 | |
---|
163 | this.view = new Ext.ListView({ |
---|
164 | multiSelect: true, |
---|
165 | store: this.store, |
---|
166 | columns: [{ header: 'Value', width: 1, dataIndex: this.displayField }], |
---|
167 | hideHeaders: true |
---|
168 | }); |
---|
169 | |
---|
170 | fs.add(this.view); |
---|
171 | |
---|
172 | this.view.on('click', this.onViewClick, this); |
---|
173 | this.view.on('beforeclick', this.onViewBeforeClick, this); |
---|
174 | this.view.on('dblclick', this.onViewDblClick, this); |
---|
175 | |
---|
176 | this.hiddenName = this.name || Ext.id(); |
---|
177 | var hiddenTag = { tag: "input", type: "hidden", value: "", name: this.hiddenName }; |
---|
178 | this.hiddenField = this.el.createChild(hiddenTag); |
---|
179 | this.hiddenField.dom.disabled = this.hiddenName != this.name; |
---|
180 | fs.doLayout(); |
---|
181 | }, |
---|
182 | |
---|
183 | // private |
---|
184 | afterRender: function(){ |
---|
185 | Ext.ux.form.MultiSelect.superclass.afterRender.call(this); |
---|
186 | |
---|
187 | if (this.ddReorder && !this.dragGroup && !this.dropGroup){ |
---|
188 | this.dragGroup = this.dropGroup = 'MultiselectDD-' + Ext.id(); |
---|
189 | } |
---|
190 | |
---|
191 | if (this.draggable || this.dragGroup){ |
---|
192 | this.dragZone = new Ext.ux.form.MultiSelect.DragZone(this, { |
---|
193 | ddGroup: this.dragGroup |
---|
194 | }); |
---|
195 | } |
---|
196 | if (this.droppable || this.dropGroup){ |
---|
197 | this.dropZone = new Ext.ux.form.MultiSelect.DropZone(this, { |
---|
198 | ddGroup: this.dropGroup |
---|
199 | }); |
---|
200 | } |
---|
201 | }, |
---|
202 | |
---|
203 | // private |
---|
204 | onViewClick: function(vw, index, node, e) { |
---|
205 | this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value); |
---|
206 | this.hiddenField.dom.value = this.getValue(); |
---|
207 | this.fireEvent('click', this, e); |
---|
208 | this.validate(); |
---|
209 | }, |
---|
210 | |
---|
211 | // private |
---|
212 | onViewBeforeClick: function(vw, index, node, e) { |
---|
213 | if (this.disabled) {return false;} |
---|
214 | }, |
---|
215 | |
---|
216 | // private |
---|
217 | onViewDblClick : function(vw, index, node, e) { |
---|
218 | return this.fireEvent('dblclick', vw, index, node, e); |
---|
219 | }, |
---|
220 | |
---|
221 | /** |
---|
222 | * Returns an array of data values for the selected items in the list. The values will be separated |
---|
223 | * by {@link #delimiter}. |
---|
224 | * @return {Array} value An array of string data values |
---|
225 | */ |
---|
226 | getValue: function(valueField){ |
---|
227 | var returnArray = []; |
---|
228 | var selectionsArray = this.view.getSelectedIndexes(); |
---|
229 | if (selectionsArray.length == 0) {return '';} |
---|
230 | for (var i=0; i<selectionsArray.length; i++) { |
---|
231 | returnArray.push(this.store.getAt(selectionsArray[i]).get((valueField != null) ? valueField : this.valueField)); |
---|
232 | } |
---|
233 | return returnArray.join(this.delimiter); |
---|
234 | }, |
---|
235 | |
---|
236 | /** |
---|
237 | * Sets a delimited string (using {@link #delimiter}) or array of data values into the list. |
---|
238 | * @param {String/Array} values The values to set |
---|
239 | */ |
---|
240 | setValue: function(values) { |
---|
241 | var index; |
---|
242 | var selections = []; |
---|
243 | this.view.clearSelections(); |
---|
244 | this.hiddenField.dom.value = ''; |
---|
245 | |
---|
246 | if (!values || (values == '')) { return; } |
---|
247 | |
---|
248 | if (!Ext.isArray(values)) { values = values.split(this.delimiter); } |
---|
249 | for (var i=0; i<values.length; i++) { |
---|
250 | index = this.view.store.indexOf(this.view.store.query(this.valueField, |
---|
251 | new RegExp('^' + values[i] + '$', "i")).itemAt(0)); |
---|
252 | selections.push(index); |
---|
253 | } |
---|
254 | this.view.select(selections); |
---|
255 | this.hiddenField.dom.value = this.getValue(); |
---|
256 | this.validate(); |
---|
257 | }, |
---|
258 | |
---|
259 | // inherit docs |
---|
260 | reset : function() { |
---|
261 | this.setValue(''); |
---|
262 | }, |
---|
263 | |
---|
264 | // inherit docs |
---|
265 | getRawValue: function(valueField) { |
---|
266 | var tmp = this.getValue(valueField); |
---|
267 | if (tmp.length) { |
---|
268 | tmp = tmp.split(this.delimiter); |
---|
269 | } |
---|
270 | else { |
---|
271 | tmp = []; |
---|
272 | } |
---|
273 | return tmp; |
---|
274 | }, |
---|
275 | |
---|
276 | // inherit docs |
---|
277 | setRawValue: function(values){ |
---|
278 | setValue(values); |
---|
279 | }, |
---|
280 | |
---|
281 | // inherit docs |
---|
282 | validateValue : function(value){ |
---|
283 | if (value.length < 1) { // if it has no value |
---|
284 | if (this.allowBlank) { |
---|
285 | this.clearInvalid(); |
---|
286 | return true; |
---|
287 | } else { |
---|
288 | this.markInvalid(this.blankText); |
---|
289 | return false; |
---|
290 | } |
---|
291 | } |
---|
292 | if (value.length < this.minSelections) { |
---|
293 | this.markInvalid(String.format(this.minSelectionsText, this.minSelections)); |
---|
294 | return false; |
---|
295 | } |
---|
296 | if (value.length > this.maxSelections) { |
---|
297 | this.markInvalid(String.format(this.maxSelectionsText, this.maxSelections)); |
---|
298 | return false; |
---|
299 | } |
---|
300 | return true; |
---|
301 | }, |
---|
302 | |
---|
303 | // inherit docs |
---|
304 | disable: function(){ |
---|
305 | this.disabled = true; |
---|
306 | this.hiddenField.dom.disabled = true; |
---|
307 | this.fs.disable(); |
---|
308 | }, |
---|
309 | |
---|
310 | // inherit docs |
---|
311 | enable: function(){ |
---|
312 | this.disabled = false; |
---|
313 | this.hiddenField.dom.disabled = false; |
---|
314 | this.fs.enable(); |
---|
315 | }, |
---|
316 | |
---|
317 | // inherit docs |
---|
318 | destroy: function(){ |
---|
319 | Ext.destroy(this.fs, this.dragZone, this.dropZone); |
---|
320 | Ext.ux.form.MultiSelect.superclass.destroy.call(this); |
---|
321 | } |
---|
322 | }); |
---|
323 | |
---|
324 | |
---|
325 | Ext.reg('multiselect', Ext.ux.form.MultiSelect); |
---|
326 | |
---|
327 | //backwards compat |
---|
328 | Ext.ux.Multiselect = Ext.ux.form.MultiSelect; |
---|
329 | |
---|
330 | |
---|
331 | Ext.ux.form.MultiSelect.DragZone = function(ms, config){ |
---|
332 | this.ms = ms; |
---|
333 | this.view = ms.view; |
---|
334 | var ddGroup = config.ddGroup || 'MultiselectDD'; |
---|
335 | var dd; |
---|
336 | if (Ext.isArray(ddGroup)){ |
---|
337 | dd = ddGroup.shift(); |
---|
338 | } else { |
---|
339 | dd = ddGroup; |
---|
340 | ddGroup = null; |
---|
341 | } |
---|
342 | Ext.ux.form.MultiSelect.DragZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd }); |
---|
343 | this.setDraggable(ddGroup); |
---|
344 | }; |
---|
345 | |
---|
346 | Ext.extend(Ext.ux.form.MultiSelect.DragZone, Ext.dd.DragZone, { |
---|
347 | onInitDrag : function(x, y){ |
---|
348 | var el = Ext.get(this.dragData.ddel.cloneNode(true)); |
---|
349 | this.proxy.update(el.dom); |
---|
350 | el.setWidth(el.child('em').getWidth()); |
---|
351 | this.onStartDrag(x, y); |
---|
352 | return true; |
---|
353 | }, |
---|
354 | |
---|
355 | // private |
---|
356 | collectSelection: function(data) { |
---|
357 | data.repairXY = Ext.fly(this.view.getSelectedNodes()[0]).getXY(); |
---|
358 | var i = 0; |
---|
359 | this.view.store.each(function(rec){ |
---|
360 | if (this.view.isSelected(i)) { |
---|
361 | var n = this.view.getNode(i); |
---|
362 | var dragNode = n.cloneNode(true); |
---|
363 | dragNode.id = Ext.id(); |
---|
364 | data.ddel.appendChild(dragNode); |
---|
365 | data.records.push(this.view.store.getAt(i)); |
---|
366 | data.viewNodes.push(n); |
---|
367 | } |
---|
368 | i++; |
---|
369 | }, this); |
---|
370 | }, |
---|
371 | |
---|
372 | // override |
---|
373 | onEndDrag: function(data, e) { |
---|
374 | var d = Ext.get(this.dragData.ddel); |
---|
375 | if (d && d.hasClass("multi-proxy")) { |
---|
376 | d.remove(); |
---|
377 | } |
---|
378 | }, |
---|
379 | |
---|
380 | // override |
---|
381 | getDragData: function(e){ |
---|
382 | var target = this.view.findItemFromChild(e.getTarget()); |
---|
383 | if(target) { |
---|
384 | if (!this.view.isSelected(target) && !e.ctrlKey && !e.shiftKey) { |
---|
385 | this.view.select(target); |
---|
386 | this.ms.setValue(this.ms.getValue()); |
---|
387 | } |
---|
388 | if (this.view.getSelectionCount() == 0 || e.ctrlKey || e.shiftKey) return false; |
---|
389 | var dragData = { |
---|
390 | sourceView: this.view, |
---|
391 | viewNodes: [], |
---|
392 | records: [] |
---|
393 | }; |
---|
394 | if (this.view.getSelectionCount() == 1) { |
---|
395 | var i = this.view.getSelectedIndexes()[0]; |
---|
396 | var n = this.view.getNode(i); |
---|
397 | dragData.viewNodes.push(dragData.ddel = n); |
---|
398 | dragData.records.push(this.view.store.getAt(i)); |
---|
399 | dragData.repairXY = Ext.fly(n).getXY(); |
---|
400 | } else { |
---|
401 | dragData.ddel = document.createElement('div'); |
---|
402 | dragData.ddel.className = 'multi-proxy'; |
---|
403 | this.collectSelection(dragData); |
---|
404 | } |
---|
405 | return dragData; |
---|
406 | } |
---|
407 | return false; |
---|
408 | }, |
---|
409 | |
---|
410 | // override the default repairXY. |
---|
411 | getRepairXY : function(e){ |
---|
412 | return this.dragData.repairXY; |
---|
413 | }, |
---|
414 | |
---|
415 | // private |
---|
416 | setDraggable: function(ddGroup){ |
---|
417 | if (!ddGroup) return; |
---|
418 | if (Ext.isArray(ddGroup)) { |
---|
419 | Ext.each(ddGroup, this.setDraggable, this); |
---|
420 | return; |
---|
421 | } |
---|
422 | this.addToGroup(ddGroup); |
---|
423 | } |
---|
424 | }); |
---|
425 | |
---|
426 | Ext.ux.form.MultiSelect.DropZone = function(ms, config){ |
---|
427 | this.ms = ms; |
---|
428 | this.view = ms.view; |
---|
429 | var ddGroup = config.ddGroup || 'MultiselectDD'; |
---|
430 | var dd; |
---|
431 | if (Ext.isArray(ddGroup)){ |
---|
432 | dd = ddGroup.shift(); |
---|
433 | } else { |
---|
434 | dd = ddGroup; |
---|
435 | ddGroup = null; |
---|
436 | } |
---|
437 | Ext.ux.form.MultiSelect.DropZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd }); |
---|
438 | this.setDroppable(ddGroup); |
---|
439 | }; |
---|
440 | |
---|
441 | Ext.extend(Ext.ux.form.MultiSelect.DropZone, Ext.dd.DropZone, { |
---|
442 | /** |
---|
443 | * Part of the Ext.dd.DropZone interface. If no target node is found, the |
---|
444 | * whole Element becomes the target, and this causes the drop gesture to append. |
---|
445 | */ |
---|
446 | getTargetFromEvent : function(e) { |
---|
447 | var target = e.getTarget(); |
---|
448 | return target; |
---|
449 | }, |
---|
450 | |
---|
451 | // private |
---|
452 | getDropPoint : function(e, n, dd){ |
---|
453 | if (n == this.ms.fs.body.dom) { return "below"; } |
---|
454 | var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight; |
---|
455 | var c = t + (b - t) / 2; |
---|
456 | var y = Ext.lib.Event.getPageY(e); |
---|
457 | if(y <= c) { |
---|
458 | return "above"; |
---|
459 | }else{ |
---|
460 | return "below"; |
---|
461 | } |
---|
462 | }, |
---|
463 | |
---|
464 | // private |
---|
465 | isValidDropPoint: function(pt, n, data) { |
---|
466 | if (!data.viewNodes || (data.viewNodes.length != 1)) { |
---|
467 | return true; |
---|
468 | } |
---|
469 | var d = data.viewNodes[0]; |
---|
470 | if (d == n) { |
---|
471 | return false; |
---|
472 | } |
---|
473 | if ((pt == "below") && (n.nextSibling == d)) { |
---|
474 | return false; |
---|
475 | } |
---|
476 | if ((pt == "above") && (n.previousSibling == d)) { |
---|
477 | return false; |
---|
478 | } |
---|
479 | return true; |
---|
480 | }, |
---|
481 | |
---|
482 | // override |
---|
483 | onNodeEnter : function(n, dd, e, data){ |
---|
484 | return false; |
---|
485 | }, |
---|
486 | |
---|
487 | // override |
---|
488 | onNodeOver : function(n, dd, e, data){ |
---|
489 | var dragElClass = this.dropNotAllowed; |
---|
490 | var pt = this.getDropPoint(e, n, dd); |
---|
491 | if (this.isValidDropPoint(pt, n, data)) { |
---|
492 | if (this.ms.appendOnly) { |
---|
493 | return "x-tree-drop-ok-below"; |
---|
494 | } |
---|
495 | |
---|
496 | // set the insert point style on the target node |
---|
497 | if (pt) { |
---|
498 | var targetElClass; |
---|
499 | if (pt == "above"){ |
---|
500 | dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above"; |
---|
501 | targetElClass = "x-view-drag-insert-above"; |
---|
502 | } else { |
---|
503 | dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below"; |
---|
504 | targetElClass = "x-view-drag-insert-below"; |
---|
505 | } |
---|
506 | if (this.lastInsertClass != targetElClass){ |
---|
507 | Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass); |
---|
508 | this.lastInsertClass = targetElClass; |
---|
509 | } |
---|
510 | } |
---|
511 | } |
---|
512 | return dragElClass; |
---|
513 | }, |
---|
514 | |
---|
515 | // private |
---|
516 | onNodeOut : function(n, dd, e, data){ |
---|
517 | this.removeDropIndicators(n); |
---|
518 | }, |
---|
519 | |
---|
520 | // private |
---|
521 | onNodeDrop : function(n, dd, e, data){ |
---|
522 | if (this.ms.fireEvent("drop", this, n, dd, e, data) === false) { |
---|
523 | return false; |
---|
524 | } |
---|
525 | var pt = this.getDropPoint(e, n, dd); |
---|
526 | if (n != this.ms.fs.body.dom) |
---|
527 | n = this.view.findItemFromChild(n); |
---|
528 | var insertAt = (this.ms.appendOnly || (n == this.ms.fs.body.dom)) ? this.view.store.getCount() : this.view.indexOf(n); |
---|
529 | if (pt == "below") { |
---|
530 | insertAt++; |
---|
531 | } |
---|
532 | |
---|
533 | var dir = false; |
---|
534 | |
---|
535 | // Validate if dragging within the same MultiSelect |
---|
536 | if (data.sourceView == this.view) { |
---|
537 | // If the first element to be inserted below is the target node, remove it |
---|
538 | if (pt == "below") { |
---|
539 | if (data.viewNodes[0] == n) { |
---|
540 | data.viewNodes.shift(); |
---|
541 | } |
---|
542 | } else { // If the last element to be inserted above is the target node, remove it |
---|
543 | if (data.viewNodes[data.viewNodes.length - 1] == n) { |
---|
544 | data.viewNodes.pop(); |
---|
545 | } |
---|
546 | } |
---|
547 | |
---|
548 | // Nothing to drop... |
---|
549 | if (!data.viewNodes.length) { |
---|
550 | return false; |
---|
551 | } |
---|
552 | |
---|
553 | // If we are moving DOWN, then because a store.remove() takes place first, |
---|
554 | // the insertAt must be decremented. |
---|
555 | if (insertAt > this.view.store.indexOf(data.records[0])) { |
---|
556 | dir = 'down'; |
---|
557 | insertAt--; |
---|
558 | } |
---|
559 | } |
---|
560 | |
---|
561 | for (var i = 0; i < data.records.length; i++) { |
---|
562 | var r = data.records[i]; |
---|
563 | if (data.sourceView) { |
---|
564 | data.sourceView.store.remove(r); |
---|
565 | } |
---|
566 | this.view.store.insert(dir == 'down' ? insertAt : insertAt++, r); |
---|
567 | var si = this.view.store.sortInfo; |
---|
568 | if(si){ |
---|
569 | this.view.store.sort(si.field, si.direction); |
---|
570 | } |
---|
571 | } |
---|
572 | return true; |
---|
573 | }, |
---|
574 | |
---|
575 | // private |
---|
576 | removeDropIndicators : function(n){ |
---|
577 | if(n){ |
---|
578 | Ext.fly(n).removeClass([ |
---|
579 | "x-view-drag-insert-above", |
---|
580 | "x-view-drag-insert-left", |
---|
581 | "x-view-drag-insert-right", |
---|
582 | "x-view-drag-insert-below"]); |
---|
583 | this.lastInsertClass = "_noclass"; |
---|
584 | } |
---|
585 | }, |
---|
586 | |
---|
587 | // private |
---|
588 | setDroppable: function(ddGroup){ |
---|
589 | if (!ddGroup) return; |
---|
590 | if (Ext.isArray(ddGroup)) { |
---|
591 | Ext.each(ddGroup, this.setDroppable, this); |
---|
592 | return; |
---|
593 | } |
---|
594 | this.addToGroup(ddGroup); |
---|
595 | } |
---|
596 | }); |
---|