Roo/bootstrap/ComboBox.js
[roojs1] / Roo / bootstrap / ComboBox.js
1 /*
2  * - LGPL
3  * * 
4  */
5
6 /**
7  * @class Roo.bootstrap.ComboBox
8  * @extends Roo.bootstrap.TriggerField
9  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10  * @cfg {Boolean} append (true|false) default false
11  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15  * @constructor
16  * Create a new ComboBox.
17  * @param {Object} config Configuration options
18  */
19 Roo.bootstrap.ComboBox = function(config){
20     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
21     this.addEvents({
22         /**
23          * @event expand
24          * Fires when the dropdown list is expanded
25              * @param {Roo.bootstrap.ComboBox} combo This combo box
26              */
27         'expand' : true,
28         /**
29          * @event collapse
30          * Fires when the dropdown list is collapsed
31              * @param {Roo.bootstrap.ComboBox} combo This combo box
32              */
33         'collapse' : true,
34         /**
35          * @event beforeselect
36          * Fires before a list item is selected. Return false to cancel the selection.
37              * @param {Roo.bootstrap.ComboBox} combo This combo box
38              * @param {Roo.data.Record} record The data record returned from the underlying store
39              * @param {Number} index The index of the selected item in the dropdown list
40              */
41         'beforeselect' : true,
42         /**
43          * @event select
44          * Fires when a list item is selected
45              * @param {Roo.bootstrap.ComboBox} combo This combo box
46              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
47              * @param {Number} index The index of the selected item in the dropdown list
48              */
49         'select' : true,
50         /**
51          * @event beforequery
52          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
53          * The event object passed has these properties:
54              * @param {Roo.bootstrap.ComboBox} combo This combo box
55              * @param {String} query The query
56              * @param {Boolean} forceAll true to force "all" query
57              * @param {Boolean} cancel true to cancel the query
58              * @param {Object} e The query event object
59              */
60         'beforequery': true,
61          /**
62          * @event add
63          * Fires when the 'add' icon is pressed (add a listener to enable add button)
64              * @param {Roo.bootstrap.ComboBox} combo This combo box
65              */
66         'add' : true,
67         /**
68          * @event edit
69          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
70              * @param {Roo.bootstrap.ComboBox} combo This combo box
71              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
72              */
73         'edit' : true,
74         /**
75          * @event remove
76          * Fires when the remove value from the combobox array
77              * @param {Roo.bootstrap.ComboBox} combo This combo box
78              */
79         'remove' : true
80         
81     });
82     
83     this.item = [];
84     this.tickItems = [];
85     
86     this.selectedIndex = -1;
87     if(this.mode == 'local'){
88         if(config.queryDelay === undefined){
89             this.queryDelay = 10;
90         }
91         if(config.minChars === undefined){
92             this.minChars = 0;
93         }
94     }
95 };
96
97 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
98      
99     /**
100      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
101      * rendering into an Roo.Editor, defaults to false)
102      */
103     /**
104      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
105      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
106      */
107     /**
108      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
109      */
110     /**
111      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
112      * the dropdown list (defaults to undefined, with no header element)
113      */
114
115      /**
116      * @cfg {String/Roo.Template} tpl The template to use to render the output
117      */
118      
119      /**
120      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
121      */
122     listWidth: undefined,
123     /**
124      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
125      * mode = 'remote' or 'text' if mode = 'local')
126      */
127     displayField: undefined,
128     /**
129      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
130      * mode = 'remote' or 'value' if mode = 'local'). 
131      * Note: use of a valueField requires the user make a selection
132      * in order for a value to be mapped.
133      */
134     valueField: undefined,
135     
136     
137     /**
138      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
139      * field's data value (defaults to the underlying DOM element's name)
140      */
141     hiddenName: undefined,
142     /**
143      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
144      */
145     listClass: '',
146     /**
147      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
148      */
149     selectedClass: 'active',
150     
151     /**
152      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
153      */
154     shadow:'sides',
155     /**
156      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
157      * anchor positions (defaults to 'tl-bl')
158      */
159     listAlign: 'tl-bl?',
160     /**
161      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
162      */
163     maxHeight: 300,
164     /**
165      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
166      * query specified by the allQuery config option (defaults to 'query')
167      */
168     triggerAction: 'query',
169     /**
170      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
171      * (defaults to 4, does not apply if editable = false)
172      */
173     minChars : 4,
174     /**
175      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
176      * delay (typeAheadDelay) if it matches a known value (defaults to false)
177      */
178     typeAhead: false,
179     /**
180      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
181      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
182      */
183     queryDelay: 500,
184     /**
185      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
186      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
187      */
188     pageSize: 0,
189     /**
190      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
191      * when editable = true (defaults to false)
192      */
193     selectOnFocus:false,
194     /**
195      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
196      */
197     queryParam: 'query',
198     /**
199      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
200      * when mode = 'remote' (defaults to 'Loading...')
201      */
202     loadingText: 'Loading...',
203     /**
204      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
205      */
206     resizable: false,
207     /**
208      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
209      */
210     handleHeight : 8,
211     /**
212      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
213      * traditional select (defaults to true)
214      */
215     editable: true,
216     /**
217      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
218      */
219     allQuery: '',
220     /**
221      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
222      */
223     mode: 'remote',
224     /**
225      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
226      * listWidth has a higher value)
227      */
228     minListWidth : 70,
229     /**
230      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
231      * allow the user to set arbitrary text into the field (defaults to false)
232      */
233     forceSelection:false,
234     /**
235      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
236      * if typeAhead = true (defaults to 250)
237      */
238     typeAheadDelay : 250,
239     /**
240      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
241      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
242      */
243     valueNotFoundText : undefined,
244     /**
245      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
246      */
247     blockFocus : false,
248     
249     /**
250      * @cfg {Boolean} disableClear Disable showing of clear button.
251      */
252     disableClear : false,
253     /**
254      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
255      */
256     alwaysQuery : false,
257     
258     /**
259      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
260      */
261     multiple : false,
262     
263     //private
264     addicon : false,
265     editicon: false,
266     
267     page: 0,
268     hasQuery: false,
269     append: false,
270     loadNext: false,
271     autoFocus : true,
272     tickable : false,
273     btnPosition : 'right',
274     triggerList : true,
275     // element that contains real text value.. (when hidden is used..)
276     
277     getAutoCreate : function()
278     {
279         var cfg = false;
280         
281         /*
282          *  Normal ComboBox
283          */
284         if(!this.tickable){
285             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
286             return cfg;
287         }
288         
289         /*
290          *  ComboBox with tickable selections
291          */
292              
293         var align = this.labelAlign || this.parentLabelAlign();
294         
295         cfg = {
296             cls : 'form-group roo-combobox-tickable' //input-group
297         };
298         
299         
300         var buttons = {
301             tag : 'div',
302             cls : 'tickable-buttons',
303             cn : [
304                 {
305                     tag : 'button',
306                     type : 'button',
307                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
308                     html : 'Edit'
309                 },
310                 {
311                     tag : 'button',
312                     type : 'button',
313                     name : 'ok',
314                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
315                     html : 'Done'
316                 },
317                 {
318                     tag : 'button',
319                     type : 'button',
320                     name : 'cancel',
321                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
322                     html : 'Cancel'
323                 }
324             ]
325         };
326         
327         var _this = this;
328         Roo.each(buttons.cn, function(c){
329             if (_this.size) {
330                 c.cls += ' btn-' + _this.size;
331             }
332
333             if (_this.disabled) {
334                 c.disabled = true;
335             }
336         });
337         
338         var box = {
339             tag: 'div',
340             cn: [
341                 {
342                     tag: 'input',
343                     type : 'hidden',
344                     cls: 'form-hidden-field'
345                 },
346                 {
347                     tag: 'ul',
348                     cls: 'select2-choices',
349                     cn:[
350                         {
351                             tag: 'li',
352                             cls: 'select2-search-field',
353                             cn: [
354
355                                 buttons
356                             ]
357                         }
358                     ]
359                 }
360             ]
361         }
362         
363         var combobox = {
364             cls: 'select2-container input-group select2-container-multi',
365             cn: [
366                 box
367 //                {
368 //                    tag: 'ul',
369 //                    cls: 'typeahead typeahead-long dropdown-menu',
370 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
371 //                }
372             ]
373         };
374         
375         if (align ==='left' && this.fieldLabel.length) {
376             
377                 Roo.log("left and has label");
378                 cfg.cn = [
379                     
380                     {
381                         tag: 'label',
382                         'for' :  id,
383                         cls : 'control-label col-sm-' + this.labelWidth,
384                         html : this.fieldLabel
385                         
386                     },
387                     {
388                         cls : "col-sm-" + (12 - this.labelWidth), 
389                         cn: [
390                             combobox
391                         ]
392                     }
393                     
394                 ];
395         } else if ( this.fieldLabel.length) {
396                 Roo.log(" label");
397                  cfg.cn = [
398                    
399                     {
400                         tag: 'label',
401                         //cls : 'input-group-addon',
402                         html : this.fieldLabel
403                         
404                     },
405                     
406                     combobox
407                     
408                 ];
409
410         } else {
411             
412                 Roo.log(" no label && no align");
413                 cfg = combobox
414                      
415                 
416         }
417          
418         var settings=this;
419         ['xs','sm','md','lg'].map(function(size){
420             if (settings[size]) {
421                 cfg.cls += ' col-' + size + '-' + settings[size];
422             }
423         });
424         
425         return cfg;
426         
427     },
428     
429     // private
430     initEvents: function()
431     {
432         
433         if (!this.store) {
434             throw "can not find store for combo";
435         }
436         this.store = Roo.factory(this.store, Roo.data);
437         
438         if(this.tickable){
439             this.initTickableEvnets();
440             return;
441         }
442         
443         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
444         
445         if(this.hiddenName){
446             
447             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
448             
449             this.hiddenField.dom.value =
450                 this.hiddenValue !== undefined ? this.hiddenValue :
451                 this.value !== undefined ? this.value : '';
452
453             // prevent input submission
454             this.el.dom.removeAttribute('name');
455             this.hiddenField.dom.setAttribute('name', this.hiddenName);
456              
457              
458         }
459         //if(Roo.isGecko){
460         //    this.el.dom.setAttribute('autocomplete', 'off');
461         //}
462         
463         var cls = 'x-combo-list';
464         
465         //this.list = new Roo.Layer({
466         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
467         //});
468         
469         var _this = this;
470         
471         (function(){
472             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
473             _this.list.setWidth(lw);
474         }).defer(100);
475         
476         this.list.on('mouseover', this.onViewOver, this);
477         this.list.on('mousemove', this.onViewMove, this);
478         
479         this.list.on('scroll', this.onViewScroll, this);
480         
481         /*
482         this.list.swallowEvent('mousewheel');
483         this.assetHeight = 0;
484
485         if(this.title){
486             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
487             this.assetHeight += this.header.getHeight();
488         }
489
490         this.innerList = this.list.createChild({cls:cls+'-inner'});
491         this.innerList.on('mouseover', this.onViewOver, this);
492         this.innerList.on('mousemove', this.onViewMove, this);
493         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
494         
495         if(this.allowBlank && !this.pageSize && !this.disableClear){
496             this.footer = this.list.createChild({cls:cls+'-ft'});
497             this.pageTb = new Roo.Toolbar(this.footer);
498            
499         }
500         if(this.pageSize){
501             this.footer = this.list.createChild({cls:cls+'-ft'});
502             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
503                     {pageSize: this.pageSize});
504             
505         }
506         
507         if (this.pageTb && this.allowBlank && !this.disableClear) {
508             var _this = this;
509             this.pageTb.add(new Roo.Toolbar.Fill(), {
510                 cls: 'x-btn-icon x-btn-clear',
511                 text: ' ',
512                 handler: function()
513                 {
514                     _this.collapse();
515                     _this.clearValue();
516                     _this.onSelect(false, -1);
517                 }
518             });
519         }
520         if (this.footer) {
521             this.assetHeight += this.footer.getHeight();
522         }
523         */
524             
525         if(!this.tpl){
526             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
527         }
528
529         this.view = new Roo.View(this.list, this.tpl, {
530             singleSelect:true, store: this.store, selectedClass: this.selectedClass
531         });
532         //this.view.wrapEl.setDisplayed(false);
533         this.view.on('click', this.onViewClick, this);
534         
535         
536         
537         this.store.on('beforeload', this.onBeforeLoad, this);
538         this.store.on('load', this.onLoad, this);
539         this.store.on('loadexception', this.onLoadException, this);
540         /*
541         if(this.resizable){
542             this.resizer = new Roo.Resizable(this.list,  {
543                pinned:true, handles:'se'
544             });
545             this.resizer.on('resize', function(r, w, h){
546                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
547                 this.listWidth = w;
548                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
549                 this.restrictHeight();
550             }, this);
551             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
552         }
553         */
554         if(!this.editable){
555             this.editable = true;
556             this.setEditable(false);
557         }
558         
559         /*
560         
561         if (typeof(this.events.add.listeners) != 'undefined') {
562             
563             this.addicon = this.wrap.createChild(
564                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
565        
566             this.addicon.on('click', function(e) {
567                 this.fireEvent('add', this);
568             }, this);
569         }
570         if (typeof(this.events.edit.listeners) != 'undefined') {
571             
572             this.editicon = this.wrap.createChild(
573                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
574             if (this.addicon) {
575                 this.editicon.setStyle('margin-left', '40px');
576             }
577             this.editicon.on('click', function(e) {
578                 
579                 // we fire even  if inothing is selected..
580                 this.fireEvent('edit', this, this.lastData );
581                 
582             }, this);
583         }
584         */
585         
586         this.keyNav = new Roo.KeyNav(this.inputEl(), {
587             "up" : function(e){
588                 this.inKeyMode = true;
589                 this.selectPrev();
590             },
591
592             "down" : function(e){
593                 if(!this.isExpanded()){
594                     this.onTriggerClick();
595                 }else{
596                     this.inKeyMode = true;
597                     this.selectNext();
598                 }
599             },
600
601             "enter" : function(e){
602 //                this.onViewClick();
603                 //return true;
604                 this.collapse();
605                 
606                 if(this.fireEvent("specialkey", this, e)){
607                     this.onViewClick(false);
608                 }
609                 
610                 return true;
611             },
612
613             "esc" : function(e){
614                 this.collapse();
615             },
616
617             "tab" : function(e){
618                 this.collapse();
619                 
620                 if(this.fireEvent("specialkey", this, e)){
621                     this.onViewClick(false);
622                 }
623                 
624                 return true;
625             },
626
627             scope : this,
628
629             doRelay : function(foo, bar, hname){
630                 if(hname == 'down' || this.scope.isExpanded()){
631                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
632                 }
633                 return true;
634             },
635
636             forceKeyDown: true
637         });
638         
639         
640         this.queryDelay = Math.max(this.queryDelay || 10,
641                 this.mode == 'local' ? 10 : 250);
642         
643         
644         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
645         
646         if(this.typeAhead){
647             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
648         }
649         if(this.editable !== false){
650             this.inputEl().on("keyup", this.onKeyUp, this);
651         }
652         if(this.forceSelection){
653             this.inputEl().on('blur', this.doForce, this);
654         }
655         
656         if(this.multiple){
657             this.choices = this.el.select('ul.select2-choices', true).first();
658             this.searchField = this.el.select('ul li.select2-search-field', true).first();
659         }
660     },
661     
662     initTickableEvnets: function()
663     {   
664         this.createList();
665         
666         if(this.hiddenName){
667             
668             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
669             
670             this.hiddenField.dom.value =
671                 this.hiddenValue !== undefined ? this.hiddenValue :
672                 this.value !== undefined ? this.value : '';
673
674             // prevent input submission
675             this.el.dom.removeAttribute('name');
676             this.hiddenField.dom.setAttribute('name', this.hiddenName);
677              
678              
679         }
680         
681 //        this.list = this.el.select('ul.dropdown-menu',true).first();
682         
683         this.choices = this.el.select('ul.select2-choices', true).first();
684         this.searchField = this.el.select('ul li.select2-search-field', true).first();
685         if(this.triggerList){
686             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
687         }
688          
689         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
690         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
691         
692         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
693         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
694         
695         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
696         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
697         
698         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
699         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
700         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
701         
702         this.okBtn.hide();
703         this.cancelBtn.hide();
704         
705         var _this = this;
706         
707         (function(){
708             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
709             _this.list.setWidth(lw);
710         }).defer(100);
711         
712         this.list.on('mouseover', this.onViewOver, this);
713         this.list.on('mousemove', this.onViewMove, this);
714         
715         this.list.on('scroll', this.onViewScroll, this);
716         
717         if(!this.tpl){
718             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
719         }
720
721         this.view = new Roo.View(this.list, this.tpl, {
722             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
723         });
724         
725         //this.view.wrapEl.setDisplayed(false);
726         this.view.on('click', this.onViewClick, this);
727         
728         
729         
730         this.store.on('beforeload', this.onBeforeLoad, this);
731         this.store.on('load', this.onLoad, this);
732         this.store.on('loadexception', this.onLoadException, this);
733         
734 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
735 //            "up" : function(e){
736 //                this.inKeyMode = true;
737 //                this.selectPrev();
738 //            },
739 //
740 //            "down" : function(e){
741 //                if(!this.isExpanded()){
742 //                    this.onTriggerClick();
743 //                }else{
744 //                    this.inKeyMode = true;
745 //                    this.selectNext();
746 //                }
747 //            },
748 //
749 //            "enter" : function(e){
750 ////                this.onViewClick();
751 //                //return true;
752 //                this.collapse();
753 //                
754 //                if(this.fireEvent("specialkey", this, e)){
755 //                    this.onViewClick(false);
756 //                }
757 //                
758 //                return true;
759 //            },
760 //
761 //            "esc" : function(e){
762 //                this.collapse();
763 //            },
764 //
765 //            "tab" : function(e){
766 //                this.collapse();
767 //                
768 //                if(this.fireEvent("specialkey", this, e)){
769 //                    this.onViewClick(false);
770 //                }
771 //                
772 //                return true;
773 //            },
774 //
775 //            scope : this,
776 //
777 //            doRelay : function(foo, bar, hname){
778 //                if(hname == 'down' || this.scope.isExpanded()){
779 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
780 //                }
781 //                return true;
782 //            },
783 //
784 //            forceKeyDown: true
785 //        });
786         
787         
788         this.queryDelay = Math.max(this.queryDelay || 10,
789                 this.mode == 'local' ? 10 : 250);
790         
791         
792         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
793         
794         if(this.typeAhead){
795             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
796         }
797     },
798
799     onDestroy : function(){
800         if(this.view){
801             this.view.setStore(null);
802             this.view.el.removeAllListeners();
803             this.view.el.remove();
804             this.view.purgeListeners();
805         }
806         if(this.list){
807             this.list.dom.innerHTML  = '';
808         }
809         
810         if(this.store){
811             this.store.un('beforeload', this.onBeforeLoad, this);
812             this.store.un('load', this.onLoad, this);
813             this.store.un('loadexception', this.onLoadException, this);
814         }
815         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
816     },
817
818     // private
819     fireKey : function(e){
820         if(e.isNavKeyPress() && !this.list.isVisible()){
821             this.fireEvent("specialkey", this, e);
822         }
823     },
824
825     // private
826     onResize: function(w, h){
827 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
828 //        
829 //        if(typeof w != 'number'){
830 //            // we do not handle it!?!?
831 //            return;
832 //        }
833 //        var tw = this.trigger.getWidth();
834 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
835 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
836 //        var x = w - tw;
837 //        this.inputEl().setWidth( this.adjustWidth('input', x));
838 //            
839 //        //this.trigger.setStyle('left', x+'px');
840 //        
841 //        if(this.list && this.listWidth === undefined){
842 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
843 //            this.list.setWidth(lw);
844 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
845 //        }
846         
847     
848         
849     },
850
851     /**
852      * Allow or prevent the user from directly editing the field text.  If false is passed,
853      * the user will only be able to select from the items defined in the dropdown list.  This method
854      * is the runtime equivalent of setting the 'editable' config option at config time.
855      * @param {Boolean} value True to allow the user to directly edit the field text
856      */
857     setEditable : function(value){
858         if(value == this.editable){
859             return;
860         }
861         this.editable = value;
862         if(!value){
863             this.inputEl().dom.setAttribute('readOnly', true);
864             this.inputEl().on('mousedown', this.onTriggerClick,  this);
865             this.inputEl().addClass('x-combo-noedit');
866         }else{
867             this.inputEl().dom.setAttribute('readOnly', false);
868             this.inputEl().un('mousedown', this.onTriggerClick,  this);
869             this.inputEl().removeClass('x-combo-noedit');
870         }
871     },
872
873     // private
874     
875     onBeforeLoad : function(combo,opts){
876         if(!this.hasFocus){
877             return;
878         }
879          if (!opts.add) {
880             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
881          }
882 //        this.restrictHeight();
883         this.selectedIndex = -1;
884     },
885
886     // private
887     onLoad : function(){
888         
889         this.hasQuery = false;
890         
891         if(!this.hasFocus){
892             return;
893         }
894         
895         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
896             this.loading.hide();
897         }
898         
899         if(this.store.getCount() > 0){
900             this.expand();
901 //            this.restrictHeight();
902             if(this.lastQuery == this.allQuery){
903                 if(this.editable && !this.tickable){
904                     this.inputEl().dom.select();
905                 }
906                 if(!this.selectByValue(this.value, true) && this.autoFocus){
907                     this.select(0, true);
908                 }
909             }else{
910                 if(this.autoFocus){
911                     this.selectNext();
912                 }
913                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
914                     this.taTask.delay(this.typeAheadDelay);
915                 }
916             }
917         }else{
918             this.onEmptyResults();
919         }
920         
921         //this.el.focus();
922     },
923     // private
924     onLoadException : function()
925     {
926         this.hasQuery = false;
927         
928         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
929             this.loading.hide();
930         }
931         
932         this.collapse();
933         Roo.log(this.store.reader.jsonData);
934         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
935             // fixme
936             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
937         }
938         
939         
940     },
941     // private
942     onTypeAhead : function(){
943         if(this.store.getCount() > 0){
944             var r = this.store.getAt(0);
945             var newValue = r.data[this.displayField];
946             var len = newValue.length;
947             var selStart = this.getRawValue().length;
948             
949             if(selStart != len){
950                 this.setRawValue(newValue);
951                 this.selectText(selStart, newValue.length);
952             }
953         }
954     },
955
956     // private
957     onSelect : function(record, index){
958         
959         if(this.fireEvent('beforeselect', this, record, index) !== false){
960         
961             this.setFromData(index > -1 ? record.data : false);
962             
963             this.collapse();
964             this.fireEvent('select', this, record, index);
965         }
966     },
967
968     /**
969      * Returns the currently selected field value or empty string if no value is set.
970      * @return {String} value The selected value
971      */
972     getValue : function(){
973         
974         if(this.multiple){
975             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
976         }
977         
978         if(this.valueField){
979             return typeof this.value != 'undefined' ? this.value : '';
980         }else{
981             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
982         }
983     },
984
985     /**
986      * Clears any text/value currently set in the field
987      */
988     clearValue : function(){
989         if(this.hiddenField){
990             this.hiddenField.dom.value = '';
991         }
992         this.value = '';
993         this.setRawValue('');
994         this.lastSelectionText = '';
995         
996     },
997
998     /**
999      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
1000      * will be displayed in the field.  If the value does not match the data value of an existing item,
1001      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
1002      * Otherwise the field will be blank (although the value will still be set).
1003      * @param {String} value The value to match
1004      */
1005     setValue : function(v){
1006         if(this.multiple){
1007             this.syncValue();
1008             return;
1009         }
1010         
1011         var text = v;
1012         if(this.valueField){
1013             var r = this.findRecord(this.valueField, v);
1014             if(r){
1015                 text = r.data[this.displayField];
1016             }else if(this.valueNotFoundText !== undefined){
1017                 text = this.valueNotFoundText;
1018             }
1019         }
1020         this.lastSelectionText = text;
1021         if(this.hiddenField){
1022             this.hiddenField.dom.value = v;
1023         }
1024         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
1025         this.value = v;
1026     },
1027     /**
1028      * @property {Object} the last set data for the element
1029      */
1030     
1031     lastData : false,
1032     /**
1033      * Sets the value of the field based on a object which is related to the record format for the store.
1034      * @param {Object} value the value to set as. or false on reset?
1035      */
1036     setFromData : function(o){
1037         
1038         if(this.multiple){
1039             this.addItem(o);
1040             return;
1041         }
1042             
1043         var dv = ''; // display value
1044         var vv = ''; // value value..
1045         this.lastData = o;
1046         if (this.displayField) {
1047             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1048         } else {
1049             // this is an error condition!!!
1050             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1051         }
1052         
1053         if(this.valueField){
1054             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
1055         }
1056         
1057         if(this.hiddenField){
1058             this.hiddenField.dom.value = vv;
1059             
1060             this.lastSelectionText = dv;
1061             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1062             this.value = vv;
1063             return;
1064         }
1065         // no hidden field.. - we store the value in 'value', but still display
1066         // display field!!!!
1067         this.lastSelectionText = dv;
1068         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1069         this.value = vv;
1070         
1071         
1072     },
1073     // private
1074     reset : function(){
1075         // overridden so that last data is reset..
1076         this.setValue(this.originalValue);
1077         this.clearInvalid();
1078         this.lastData = false;
1079         if (this.view) {
1080             this.view.clearSelections();
1081         }
1082     },
1083     // private
1084     findRecord : function(prop, value){
1085         var record;
1086         if(this.store.getCount() > 0){
1087             this.store.each(function(r){
1088                 if(r.data[prop] == value){
1089                     record = r;
1090                     return false;
1091                 }
1092                 return true;
1093             });
1094         }
1095         return record;
1096     },
1097     
1098     getName: function()
1099     {
1100         // returns hidden if it's set..
1101         if (!this.rendered) {return ''};
1102         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
1103         
1104     },
1105     // private
1106     onViewMove : function(e, t){
1107         this.inKeyMode = false;
1108     },
1109
1110     // private
1111     onViewOver : function(e, t){
1112         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
1113             return;
1114         }
1115         var item = this.view.findItemFromChild(t);
1116         
1117         if(item){
1118             var index = this.view.indexOf(item);
1119             this.select(index, false);
1120         }
1121     },
1122
1123     // private
1124     onViewClick : function(view, doFocus, el, e)
1125     {
1126         var index = this.view.getSelectedIndexes()[0];
1127         
1128         var r = this.store.getAt(index);
1129         
1130         if(this.tickable){
1131             
1132             if(e.getTarget().nodeName.toLowerCase() != 'input'){
1133                 return;
1134             }
1135             
1136             var rm = false;
1137             var _this = this;
1138             
1139             Roo.each(this.tickItems, function(v,k){
1140                 
1141                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
1142                     _this.tickItems.splice(k, 1);
1143                     rm = true;
1144                     return;
1145                 }
1146             })
1147             
1148             if(rm){
1149                 return;
1150             }
1151             
1152             this.tickItems.push(r.data);
1153             return;
1154         }
1155         
1156         if(r){
1157             this.onSelect(r, index);
1158         }
1159         if(doFocus !== false && !this.blockFocus){
1160             this.inputEl().focus();
1161         }
1162     },
1163
1164     // private
1165     restrictHeight : function(){
1166         //this.innerList.dom.style.height = '';
1167         //var inner = this.innerList.dom;
1168         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
1169         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
1170         //this.list.beginUpdate();
1171         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
1172         this.list.alignTo(this.inputEl(), this.listAlign);
1173         this.list.alignTo(this.inputEl(), this.listAlign);
1174         //this.list.endUpdate();
1175     },
1176
1177     // private
1178     onEmptyResults : function(){
1179         this.collapse();
1180     },
1181
1182     /**
1183      * Returns true if the dropdown list is expanded, else false.
1184      */
1185     isExpanded : function(){
1186         return this.list.isVisible();
1187     },
1188
1189     /**
1190      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
1191      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1192      * @param {String} value The data value of the item to select
1193      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1194      * selected item if it is not currently in view (defaults to true)
1195      * @return {Boolean} True if the value matched an item in the list, else false
1196      */
1197     selectByValue : function(v, scrollIntoView){
1198         if(v !== undefined && v !== null){
1199             var r = this.findRecord(this.valueField || this.displayField, v);
1200             if(r){
1201                 this.select(this.store.indexOf(r), scrollIntoView);
1202                 return true;
1203             }
1204         }
1205         return false;
1206     },
1207
1208     /**
1209      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
1210      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1211      * @param {Number} index The zero-based index of the list item to select
1212      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1213      * selected item if it is not currently in view (defaults to true)
1214      */
1215     select : function(index, scrollIntoView){
1216         this.selectedIndex = index;
1217         this.view.select(index);
1218         if(scrollIntoView !== false){
1219             var el = this.view.getNode(index);
1220             if(el && !this.multiple && !this.tickable){
1221                 this.list.scrollChildIntoView(el, false);
1222             }
1223         }
1224     },
1225
1226     // private
1227     selectNext : function(){
1228         var ct = this.store.getCount();
1229         if(ct > 0){
1230             if(this.selectedIndex == -1){
1231                 this.select(0);
1232             }else if(this.selectedIndex < ct-1){
1233                 this.select(this.selectedIndex+1);
1234             }
1235         }
1236     },
1237
1238     // private
1239     selectPrev : function(){
1240         var ct = this.store.getCount();
1241         if(ct > 0){
1242             if(this.selectedIndex == -1){
1243                 this.select(0);
1244             }else if(this.selectedIndex != 0){
1245                 this.select(this.selectedIndex-1);
1246             }
1247         }
1248     },
1249
1250     // private
1251     onKeyUp : function(e){
1252         if(this.editable !== false && !e.isSpecialKey()){
1253             this.lastKey = e.getKey();
1254             this.dqTask.delay(this.queryDelay);
1255         }
1256     },
1257
1258     // private
1259     validateBlur : function(){
1260         return !this.list || !this.list.isVisible();   
1261     },
1262
1263     // private
1264     initQuery : function(){
1265         this.doQuery(this.getRawValue());
1266     },
1267
1268     // private
1269     doForce : function(){
1270         if(this.inputEl().dom.value.length > 0){
1271             this.inputEl().dom.value =
1272                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
1273              
1274         }
1275     },
1276
1277     /**
1278      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
1279      * query allowing the query action to be canceled if needed.
1280      * @param {String} query The SQL query to execute
1281      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
1282      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
1283      * saved in the current store (defaults to false)
1284      */
1285     doQuery : function(q, forceAll){
1286         
1287         if(q === undefined || q === null){
1288             q = '';
1289         }
1290         var qe = {
1291             query: q,
1292             forceAll: forceAll,
1293             combo: this,
1294             cancel:false
1295         };
1296         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
1297             return false;
1298         }
1299         q = qe.query;
1300         
1301         forceAll = qe.forceAll;
1302         if(forceAll === true || (q.length >= this.minChars)){
1303             
1304             this.hasQuery = true;
1305             
1306             if(this.lastQuery != q || this.alwaysQuery){
1307                 this.lastQuery = q;
1308                 if(this.mode == 'local'){
1309                     this.selectedIndex = -1;
1310                     if(forceAll){
1311                         this.store.clearFilter();
1312                     }else{
1313                         this.store.filter(this.displayField, q);
1314                     }
1315                     this.onLoad();
1316                 }else{
1317                     this.store.baseParams[this.queryParam] = q;
1318                     
1319                     var options = {params : this.getParams(q)};
1320                     
1321                     if(this.loadNext){
1322                         options.add = true;
1323                         options.params.start = this.page * this.pageSize;
1324                     }
1325                     
1326                     this.store.load(options);
1327                     /*
1328                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
1329                      *  we should expand the list on onLoad
1330                      *  so command out it
1331                      */
1332 //                    this.expand();
1333                 }
1334             }else{
1335                 this.selectedIndex = -1;
1336                 this.onLoad();   
1337             }
1338         }
1339         
1340         this.loadNext = false;
1341     },
1342
1343     // private
1344     getParams : function(q){
1345         var p = {};
1346         //p[this.queryParam] = q;
1347         
1348         if(this.pageSize){
1349             p.start = 0;
1350             p.limit = this.pageSize;
1351         }
1352         return p;
1353     },
1354
1355     /**
1356      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
1357      */
1358     collapse : function(){
1359         if(!this.isExpanded()){
1360             return;
1361         }
1362         
1363         this.hasFocus = false;
1364         
1365         this.list.hide();
1366         
1367         if(this.tickable){
1368             this.okBtn.hide();
1369             this.cancelBtn.hide();
1370             this.trigger.show();
1371         }
1372         
1373         Roo.get(document).un('mousedown', this.collapseIf, this);
1374         Roo.get(document).un('mousewheel', this.collapseIf, this);
1375         if (!this.editable) {
1376             Roo.get(document).un('keydown', this.listKeyPress, this);
1377         }
1378         this.fireEvent('collapse', this);
1379     },
1380
1381     // private
1382     collapseIf : function(e){
1383         var in_combo  = e.within(this.el);
1384         var in_list =  e.within(this.list);
1385         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
1386         
1387         if (in_combo || in_list || is_list) {
1388             //e.stopPropagation();
1389             return;
1390         }
1391         
1392         if(this.tickable){
1393             this.onTickableFooterButtonClick(e, false, false);
1394         }
1395
1396         this.collapse();
1397         
1398     },
1399
1400     /**
1401      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
1402      */
1403     expand : function(){
1404        
1405         if(this.isExpanded() || !this.hasFocus){
1406             return;
1407         }
1408          Roo.log('expand');
1409         
1410         this.list.show();
1411         
1412         this.restrictHeight();
1413         
1414         if(this.tickable){
1415             
1416             this.tickItems = Roo.apply([], this.item);
1417             
1418             this.okBtn.show();
1419             this.cancelBtn.show();
1420             this.trigger.hide();
1421             
1422         }
1423         
1424         Roo.get(document).on('mousedown', this.collapseIf, this);
1425         Roo.get(document).on('mousewheel', this.collapseIf, this);
1426         if (!this.editable) {
1427             Roo.get(document).on('keydown', this.listKeyPress, this);
1428         }
1429         
1430         this.fireEvent('expand', this);
1431     },
1432
1433     // private
1434     // Implements the default empty TriggerField.onTriggerClick function
1435     onTriggerClick : function(e)
1436     {
1437         Roo.log('trigger click');
1438         
1439         if(this.disabled || !this.triggerList){
1440             return;
1441         }
1442         
1443         this.page = 0;
1444         this.loadNext = false;
1445         
1446         if(this.isExpanded()){
1447             this.collapse();
1448             if (!this.blockFocus) {
1449                 this.inputEl().focus();
1450             }
1451             
1452         }else {
1453             this.hasFocus = true;
1454             if(this.triggerAction == 'all') {
1455                 this.doQuery(this.allQuery, true);
1456             } else {
1457                 this.doQuery(this.getRawValue());
1458             }
1459             if (!this.blockFocus) {
1460                 this.inputEl().focus();
1461             }
1462         }
1463     },
1464     
1465     onTickableTriggerClick : function(e)
1466     {
1467         if(this.disabled){
1468             return;
1469         }
1470         
1471         this.page = 0;
1472         this.loadNext = false;
1473         this.hasFocus = true;
1474         
1475         if(this.triggerAction == 'all') {
1476             this.doQuery(this.allQuery, true);
1477         } else {
1478             this.doQuery(this.getRawValue());
1479         }
1480     },
1481     
1482     onSearchFieldClick : function(e)
1483     {
1484         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
1485             return;
1486         }
1487         
1488         this.page = 0;
1489         this.loadNext = false;
1490         this.hasFocus = true;
1491         
1492         if(this.triggerAction == 'all') {
1493             this.doQuery(this.allQuery, true);
1494         } else {
1495             this.doQuery(this.getRawValue());
1496         }
1497     },
1498     
1499     listKeyPress : function(e)
1500     {
1501         //Roo.log('listkeypress');
1502         // scroll to first matching element based on key pres..
1503         if (e.isSpecialKey()) {
1504             return false;
1505         }
1506         var k = String.fromCharCode(e.getKey()).toUpperCase();
1507         //Roo.log(k);
1508         var match  = false;
1509         var csel = this.view.getSelectedNodes();
1510         var cselitem = false;
1511         if (csel.length) {
1512             var ix = this.view.indexOf(csel[0]);
1513             cselitem  = this.store.getAt(ix);
1514             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
1515                 cselitem = false;
1516             }
1517             
1518         }
1519         
1520         this.store.each(function(v) { 
1521             if (cselitem) {
1522                 // start at existing selection.
1523                 if (cselitem.id == v.id) {
1524                     cselitem = false;
1525                 }
1526                 return true;
1527             }
1528                 
1529             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
1530                 match = this.store.indexOf(v);
1531                 return false;
1532             }
1533             return true;
1534         }, this);
1535         
1536         if (match === false) {
1537             return true; // no more action?
1538         }
1539         // scroll to?
1540         this.view.select(match);
1541         var sn = Roo.get(this.view.getSelectedNodes()[0])
1542         //sn.scrollIntoView(sn.dom.parentNode, false);
1543     },
1544     
1545     onViewScroll : function(e, t){
1546         
1547         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
1548             return;
1549         }
1550         
1551         this.hasQuery = true;
1552         
1553         this.loading = this.list.select('.loading', true).first();
1554         
1555         if(this.loading === null){
1556             this.list.createChild({
1557                 tag: 'div',
1558                 cls: 'loading select2-more-results select2-active',
1559                 html: 'Loading more results...'
1560             })
1561             
1562             this.loading = this.list.select('.loading', true).first();
1563             
1564             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
1565             
1566             this.loading.hide();
1567         }
1568         
1569         this.loading.show();
1570         
1571         var _combo = this;
1572         
1573         this.page++;
1574         this.loadNext = true;
1575         
1576         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
1577         
1578         return;
1579     },
1580     
1581     addItem : function(o)
1582     {   
1583         var dv = ''; // display value
1584         
1585         if (this.displayField) {
1586             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1587         } else {
1588             // this is an error condition!!!
1589             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1590         }
1591         
1592         if(!dv.length){
1593             return;
1594         }
1595         
1596         var choice = this.choices.createChild({
1597             tag: 'li',
1598             cls: 'select2-search-choice',
1599             cn: [
1600                 {
1601                     tag: 'div',
1602                     html: dv
1603                 },
1604                 {
1605                     tag: 'a',
1606                     href: '#',
1607                     cls: 'select2-search-choice-close',
1608                     tabindex: '-1'
1609                 }
1610             ]
1611             
1612         }, this.searchField);
1613         
1614         var close = choice.select('a.select2-search-choice-close', true).first()
1615         
1616         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
1617         
1618         this.item.push(o);
1619         
1620         this.lastData = o;
1621         
1622         this.syncValue();
1623         
1624         this.inputEl().dom.value = '';
1625         
1626     },
1627     
1628     onRemoveItem : function(e, _self, o)
1629     {
1630         e.preventDefault();
1631         var index = this.item.indexOf(o.data) * 1;
1632         
1633         if( index < 0){
1634             Roo.log('not this item?!');
1635             return;
1636         }
1637         
1638         this.item.splice(index, 1);
1639         o.item.remove();
1640         
1641         this.syncValue();
1642         
1643         this.fireEvent('remove', this, e);
1644         
1645     },
1646     
1647     syncValue : function()
1648     {
1649         if(!this.item.length){
1650             this.clearValue();
1651             return;
1652         }
1653             
1654         var value = [];
1655         var _this = this;
1656         Roo.each(this.item, function(i){
1657             if(_this.valueField){
1658                 value.push(i[_this.valueField]);
1659                 return;
1660             }
1661
1662             value.push(i);
1663         });
1664
1665         this.value = value.join(',');
1666
1667         if(this.hiddenField){
1668             this.hiddenField.dom.value = this.value;
1669         }
1670     },
1671     
1672     clearItem : function()
1673     {
1674         if(!this.multiple){
1675             return;
1676         }
1677         
1678         this.item = [];
1679         
1680         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
1681            c.remove();
1682         });
1683         
1684         this.syncValue();
1685     },
1686     
1687     inputEl: function ()
1688     {
1689         if(this.tickable){
1690             return this.searchField;
1691         }
1692         return this.el.select('input.form-control',true).first();
1693     },
1694     
1695     
1696     onTickableFooterButtonClick : function(e, btn, el)
1697     {
1698         e.preventDefault();
1699         
1700         if(btn && btn.name == 'cancel'){
1701             this.tickItems = Roo.apply([], this.item);
1702             this.collapse();
1703             return;
1704         }
1705         
1706         this.clearItem();
1707         
1708         var _this = this;
1709         
1710         Roo.each(this.tickItems, function(o){
1711             _this.addItem(o);
1712         });
1713         
1714         this.collapse();
1715         
1716     }
1717     
1718     
1719
1720     /** 
1721     * @cfg {Boolean} grow 
1722     * @hide 
1723     */
1724     /** 
1725     * @cfg {Number} growMin 
1726     * @hide 
1727     */
1728     /** 
1729     * @cfg {Number} growMax 
1730     * @hide 
1731     */
1732     /**
1733      * @hide
1734      * @method autoSize
1735      */
1736 });