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