cbaa3b982b9107aef49c99945adbd03b15f0efad
[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         
685         this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
686          
687         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
688         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
689         
690         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
691         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
692         
693         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
694         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
695         
696         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
697         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
698         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
699         
700         this.okBtn.hide();
701         this.cancelBtn.hide();
702         
703         var _this = this;
704         
705         (function(){
706             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
707             _this.list.setWidth(lw);
708         }).defer(100);
709         
710         this.list.on('mouseover', this.onViewOver, this);
711         this.list.on('mousemove', this.onViewMove, this);
712         
713         this.list.on('scroll', this.onViewScroll, this);
714         
715         if(!this.tpl){
716             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>';
717         }
718
719         this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
720             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
721         });
722         
723         //this.view.wrapEl.setDisplayed(false);
724         this.view.on('click', this.onViewClick, this);
725         
726         
727         
728         this.store.on('beforeload', this.onBeforeLoad, this);
729         this.store.on('load', this.onLoad, this);
730         this.store.on('loadexception', this.onLoadException, this);
731         
732 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
733 //            "up" : function(e){
734 //                this.inKeyMode = true;
735 //                this.selectPrev();
736 //            },
737 //
738 //            "down" : function(e){
739 //                if(!this.isExpanded()){
740 //                    this.onTriggerClick();
741 //                }else{
742 //                    this.inKeyMode = true;
743 //                    this.selectNext();
744 //                }
745 //            },
746 //
747 //            "enter" : function(e){
748 ////                this.onViewClick();
749 //                //return true;
750 //                this.collapse();
751 //                
752 //                if(this.fireEvent("specialkey", this, e)){
753 //                    this.onViewClick(false);
754 //                }
755 //                
756 //                return true;
757 //            },
758 //
759 //            "esc" : function(e){
760 //                this.collapse();
761 //            },
762 //
763 //            "tab" : function(e){
764 //                this.collapse();
765 //                
766 //                if(this.fireEvent("specialkey", this, e)){
767 //                    this.onViewClick(false);
768 //                }
769 //                
770 //                return true;
771 //            },
772 //
773 //            scope : this,
774 //
775 //            doRelay : function(foo, bar, hname){
776 //                if(hname == 'down' || this.scope.isExpanded()){
777 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
778 //                }
779 //                return true;
780 //            },
781 //
782 //            forceKeyDown: true
783 //        });
784         
785         
786         this.queryDelay = Math.max(this.queryDelay || 10,
787                 this.mode == 'local' ? 10 : 250);
788         
789         
790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
791         
792         if(this.typeAhead){
793             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
794         }
795     },
796
797     onDestroy : function(){
798         if(this.view){
799             this.view.setStore(null);
800             this.view.el.removeAllListeners();
801             this.view.el.remove();
802             this.view.purgeListeners();
803         }
804         if(this.list){
805             this.list.dom.innerHTML  = '';
806         }
807         
808         if(this.store){
809             this.store.un('beforeload', this.onBeforeLoad, this);
810             this.store.un('load', this.onLoad, this);
811             this.store.un('loadexception', this.onLoadException, this);
812         }
813         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
814     },
815
816     // private
817     fireKey : function(e){
818         if(e.isNavKeyPress() && !this.list.isVisible()){
819             this.fireEvent("specialkey", this, e);
820         }
821     },
822
823     // private
824     onResize: function(w, h){
825 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
826 //        
827 //        if(typeof w != 'number'){
828 //            // we do not handle it!?!?
829 //            return;
830 //        }
831 //        var tw = this.trigger.getWidth();
832 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
833 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
834 //        var x = w - tw;
835 //        this.inputEl().setWidth( this.adjustWidth('input', x));
836 //            
837 //        //this.trigger.setStyle('left', x+'px');
838 //        
839 //        if(this.list && this.listWidth === undefined){
840 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
841 //            this.list.setWidth(lw);
842 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
843 //        }
844         
845     
846         
847     },
848
849     /**
850      * Allow or prevent the user from directly editing the field text.  If false is passed,
851      * the user will only be able to select from the items defined in the dropdown list.  This method
852      * is the runtime equivalent of setting the 'editable' config option at config time.
853      * @param {Boolean} value True to allow the user to directly edit the field text
854      */
855     setEditable : function(value){
856         if(value == this.editable){
857             return;
858         }
859         this.editable = value;
860         if(!value){
861             this.inputEl().dom.setAttribute('readOnly', true);
862             this.inputEl().on('mousedown', this.onTriggerClick,  this);
863             this.inputEl().addClass('x-combo-noedit');
864         }else{
865             this.inputEl().dom.setAttribute('readOnly', false);
866             this.inputEl().un('mousedown', this.onTriggerClick,  this);
867             this.inputEl().removeClass('x-combo-noedit');
868         }
869     },
870
871     // private
872     
873     onBeforeLoad : function(combo,opts){
874         if(!this.hasFocus){
875             return;
876         }
877          if (!opts.add) {
878             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
879          }
880         this.restrictHeight();
881         this.selectedIndex = -1;
882     },
883
884     // private
885     onLoad : function(){
886         
887         this.hasQuery = false;
888         
889         if(!this.hasFocus){
890             return;
891         }
892         
893         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
894             this.loading.hide();
895         }
896         
897         if(this.store.getCount() > 0){
898             this.expand();
899             this.restrictHeight();
900             if(this.lastQuery == this.allQuery){
901                 if(this.editable && !this.tickable){
902                     this.inputEl().dom.select();
903                 }
904                 if(!this.selectByValue(this.value, true) && this.autoFocus){
905                     this.select(0, true);
906                 }
907             }else{
908                 if(this.autoFocus){
909                     this.selectNext();
910                 }
911                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
912                     this.taTask.delay(this.typeAheadDelay);
913                 }
914             }
915         }else{
916             this.onEmptyResults();
917         }
918         
919         //this.el.focus();
920     },
921     // private
922     onLoadException : function()
923     {
924         this.hasQuery = false;
925         
926         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
927             this.loading.hide();
928         }
929         
930         this.collapse();
931         Roo.log(this.store.reader.jsonData);
932         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
933             // fixme
934             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
935         }
936         
937         
938     },
939     // private
940     onTypeAhead : function(){
941         if(this.store.getCount() > 0){
942             var r = this.store.getAt(0);
943             var newValue = r.data[this.displayField];
944             var len = newValue.length;
945             var selStart = this.getRawValue().length;
946             
947             if(selStart != len){
948                 this.setRawValue(newValue);
949                 this.selectText(selStart, newValue.length);
950             }
951         }
952     },
953
954     // private
955     onSelect : function(record, index){
956         
957         if(this.fireEvent('beforeselect', this, record, index) !== false){
958         
959             this.setFromData(index > -1 ? record.data : false);
960             
961             this.collapse();
962             this.fireEvent('select', this, record, index);
963         }
964     },
965
966     /**
967      * Returns the currently selected field value or empty string if no value is set.
968      * @return {String} value The selected value
969      */
970     getValue : function(){
971         
972         if(this.multiple){
973             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
974         }
975         
976         if(this.valueField){
977             return typeof this.value != 'undefined' ? this.value : '';
978         }else{
979             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
980         }
981     },
982
983     /**
984      * Clears any text/value currently set in the field
985      */
986     clearValue : function(){
987         if(this.hiddenField){
988             this.hiddenField.dom.value = '';
989         }
990         this.value = '';
991         this.setRawValue('');
992         this.lastSelectionText = '';
993         
994     },
995
996     /**
997      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
998      * will be displayed in the field.  If the value does not match the data value of an existing item,
999      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
1000      * Otherwise the field will be blank (although the value will still be set).
1001      * @param {String} value The value to match
1002      */
1003     setValue : function(v){
1004         if(this.multiple){
1005             this.syncValue();
1006             return;
1007         }
1008         
1009         var text = v;
1010         if(this.valueField){
1011             var r = this.findRecord(this.valueField, v);
1012             if(r){
1013                 text = r.data[this.displayField];
1014             }else if(this.valueNotFoundText !== undefined){
1015                 text = this.valueNotFoundText;
1016             }
1017         }
1018         this.lastSelectionText = text;
1019         if(this.hiddenField){
1020             this.hiddenField.dom.value = v;
1021         }
1022         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
1023         this.value = v;
1024     },
1025     /**
1026      * @property {Object} the last set data for the element
1027      */
1028     
1029     lastData : false,
1030     /**
1031      * Sets the value of the field based on a object which is related to the record format for the store.
1032      * @param {Object} value the value to set as. or false on reset?
1033      */
1034     setFromData : function(o){
1035         
1036         if(this.multiple){
1037             this.addItem(o);
1038             return;
1039         }
1040             
1041         var dv = ''; // display value
1042         var vv = ''; // value value..
1043         this.lastData = o;
1044         if (this.displayField) {
1045             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1046         } else {
1047             // this is an error condition!!!
1048             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1049         }
1050         
1051         if(this.valueField){
1052             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
1053         }
1054         
1055         if(this.hiddenField){
1056             this.hiddenField.dom.value = vv;
1057             
1058             this.lastSelectionText = dv;
1059             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1060             this.value = vv;
1061             return;
1062         }
1063         // no hidden field.. - we store the value in 'value', but still display
1064         // display field!!!!
1065         this.lastSelectionText = dv;
1066         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1067         this.value = vv;
1068         
1069         
1070     },
1071     // private
1072     reset : function(){
1073         // overridden so that last data is reset..
1074         this.setValue(this.originalValue);
1075         this.clearInvalid();
1076         this.lastData = false;
1077         if (this.view) {
1078             this.view.clearSelections();
1079         }
1080     },
1081     // private
1082     findRecord : function(prop, value){
1083         var record;
1084         if(this.store.getCount() > 0){
1085             this.store.each(function(r){
1086                 if(r.data[prop] == value){
1087                     record = r;
1088                     return false;
1089                 }
1090                 return true;
1091             });
1092         }
1093         return record;
1094     },
1095     
1096     getName: function()
1097     {
1098         // returns hidden if it's set..
1099         if (!this.rendered) {return ''};
1100         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
1101         
1102     },
1103     // private
1104     onViewMove : function(e, t){
1105         this.inKeyMode = false;
1106     },
1107
1108     // private
1109     onViewOver : function(e, t){
1110         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
1111             return;
1112         }
1113         var item = this.view.findItemFromChild(t);
1114         
1115         if(item){
1116             var index = this.view.indexOf(item);
1117             this.select(index, false);
1118         }
1119     },
1120
1121     // private
1122     onViewClick : function(view, doFocus, el, e)
1123     {
1124         var index = this.view.getSelectedIndexes()[0];
1125         
1126         var r = this.store.getAt(index);
1127         
1128         if(this.tickable){
1129             
1130             if(e.getTarget().nodeName.toLowerCase() != 'input'){
1131                 return;
1132             }
1133             
1134             var rm = false;
1135             var _this = this;
1136             
1137             Roo.each(this.tickItems, function(v,k){
1138                 
1139                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
1140                     _this.tickItems.splice(k, 1);
1141                     rm = true;
1142                     return;
1143                 }
1144             })
1145             
1146             if(rm){
1147                 return;
1148             }
1149             
1150             this.tickItems.push(r.data);
1151             return;
1152         }
1153         
1154         if(r){
1155             this.onSelect(r, index);
1156         }
1157         if(doFocus !== false && !this.blockFocus){
1158             this.inputEl().focus();
1159         }
1160     },
1161
1162     // private
1163     restrictHeight : function(){
1164         //this.innerList.dom.style.height = '';
1165         //var inner = this.innerList.dom;
1166         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
1167         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
1168         //this.list.beginUpdate();
1169         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
1170         this.list.alignTo(this.inputEl(), this.listAlign);
1171         //this.list.endUpdate();
1172     },
1173
1174     // private
1175     onEmptyResults : function(){
1176         this.collapse();
1177     },
1178
1179     /**
1180      * Returns true if the dropdown list is expanded, else false.
1181      */
1182     isExpanded : function(){
1183         return this.list.isVisible();
1184     },
1185
1186     /**
1187      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
1188      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1189      * @param {String} value The data value of the item to select
1190      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1191      * selected item if it is not currently in view (defaults to true)
1192      * @return {Boolean} True if the value matched an item in the list, else false
1193      */
1194     selectByValue : function(v, scrollIntoView){
1195         if(v !== undefined && v !== null){
1196             var r = this.findRecord(this.valueField || this.displayField, v);
1197             if(r){
1198                 this.select(this.store.indexOf(r), scrollIntoView);
1199                 return true;
1200             }
1201         }
1202         return false;
1203     },
1204
1205     /**
1206      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
1207      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1208      * @param {Number} index The zero-based index of the list item to select
1209      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1210      * selected item if it is not currently in view (defaults to true)
1211      */
1212     select : function(index, scrollIntoView){
1213         this.selectedIndex = index;
1214         this.view.select(index);
1215         if(scrollIntoView !== false){
1216             var el = this.view.getNode(index);
1217             if(el){
1218                 //this.innerList.scrollChildIntoView(el, false);
1219                 
1220             }
1221         }
1222     },
1223
1224     // private
1225     selectNext : function(){
1226         var ct = this.store.getCount();
1227         if(ct > 0){
1228             if(this.selectedIndex == -1){
1229                 this.select(0);
1230             }else if(this.selectedIndex < ct-1){
1231                 this.select(this.selectedIndex+1);
1232             }
1233         }
1234     },
1235
1236     // private
1237     selectPrev : function(){
1238         var ct = this.store.getCount();
1239         if(ct > 0){
1240             if(this.selectedIndex == -1){
1241                 this.select(0);
1242             }else if(this.selectedIndex != 0){
1243                 this.select(this.selectedIndex-1);
1244             }
1245         }
1246     },
1247
1248     // private
1249     onKeyUp : function(e){
1250         if(this.editable !== false && !e.isSpecialKey()){
1251             this.lastKey = e.getKey();
1252             this.dqTask.delay(this.queryDelay);
1253         }
1254     },
1255
1256     // private
1257     validateBlur : function(){
1258         return !this.list || !this.list.isVisible();   
1259     },
1260
1261     // private
1262     initQuery : function(){
1263         this.doQuery(this.getRawValue());
1264     },
1265
1266     // private
1267     doForce : function(){
1268         if(this.inputEl().dom.value.length > 0){
1269             this.inputEl().dom.value =
1270                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
1271              
1272         }
1273     },
1274
1275     /**
1276      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
1277      * query allowing the query action to be canceled if needed.
1278      * @param {String} query The SQL query to execute
1279      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
1280      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
1281      * saved in the current store (defaults to false)
1282      */
1283     doQuery : function(q, forceAll){
1284         
1285         if(q === undefined || q === null){
1286             q = '';
1287         }
1288         var qe = {
1289             query: q,
1290             forceAll: forceAll,
1291             combo: this,
1292             cancel:false
1293         };
1294         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
1295             return false;
1296         }
1297         q = qe.query;
1298         
1299         forceAll = qe.forceAll;
1300         if(forceAll === true || (q.length >= this.minChars)){
1301             
1302             this.hasQuery = true;
1303             
1304             if(this.lastQuery != q || this.alwaysQuery){
1305                 this.lastQuery = q;
1306                 if(this.mode == 'local'){
1307                     this.selectedIndex = -1;
1308                     if(forceAll){
1309                         this.store.clearFilter();
1310                     }else{
1311                         this.store.filter(this.displayField, q);
1312                     }
1313                     this.onLoad();
1314                 }else{
1315                     this.store.baseParams[this.queryParam] = q;
1316                     
1317                     var options = {params : this.getParams(q)};
1318                     
1319                     if(this.loadNext){
1320                         options.add = true;
1321                         options.params.start = this.page * this.pageSize;
1322                     }
1323                     
1324                     this.store.load(options);
1325                     /*
1326                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
1327                      *  we should expand the list on onLoad
1328                      *  so command out it
1329                      */
1330 //                    this.expand();
1331                 }
1332             }else{
1333                 this.selectedIndex = -1;
1334                 this.onLoad();   
1335             }
1336         }
1337         
1338         this.loadNext = false;
1339     },
1340
1341     // private
1342     getParams : function(q){
1343         var p = {};
1344         //p[this.queryParam] = q;
1345         
1346         if(this.pageSize){
1347             p.start = 0;
1348             p.limit = this.pageSize;
1349         }
1350         return p;
1351     },
1352
1353     /**
1354      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
1355      */
1356     collapse : function(){
1357         if(!this.isExpanded()){
1358             return;
1359         }
1360         
1361         this.hasFocus = false;
1362         
1363         this.list.hide();
1364         
1365         if(this.tickable){
1366             this.okBtn.hide();
1367             this.cancelBtn.hide();
1368             this.trigger.show();
1369         }
1370         
1371         Roo.get(document).un('mousedown', this.collapseIf, this);
1372         Roo.get(document).un('mousewheel', this.collapseIf, this);
1373         if (!this.editable) {
1374             Roo.get(document).un('keydown', this.listKeyPress, this);
1375         }
1376         this.fireEvent('collapse', this);
1377     },
1378
1379     // private
1380     collapseIf : function(e){
1381         var in_combo  = e.within(this.el);
1382         var in_list =  e.within(this.list);
1383         
1384         if (in_combo || in_list) {
1385             //e.stopPropagation();
1386             return;
1387         }
1388         
1389         if(this.tickable){
1390             this.onTickableFooterButtonClick(e, false, false);
1391         }
1392
1393         this.collapse();
1394         
1395     },
1396
1397     /**
1398      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
1399      */
1400     expand : function(){
1401        
1402         if(this.isExpanded() || !this.hasFocus){
1403             return;
1404         }
1405          Roo.log('expand');
1406         this.list.alignTo(this.inputEl(), this.listAlign);
1407         this.list.show();
1408         
1409         if(this.tickable){
1410             
1411             this.tickItems = Roo.apply([], this.item);
1412             
1413             this.okBtn.show();
1414             this.cancelBtn.show();
1415             this.trigger.hide();
1416             
1417         }
1418         
1419         Roo.get(document).on('mousedown', this.collapseIf, this);
1420         Roo.get(document).on('mousewheel', this.collapseIf, this);
1421         if (!this.editable) {
1422             Roo.get(document).on('keydown', this.listKeyPress, this);
1423         }
1424         
1425         this.fireEvent('expand', this);
1426     },
1427
1428     // private
1429     // Implements the default empty TriggerField.onTriggerClick function
1430     onTriggerClick : function(e)
1431     {
1432         Roo.log('trigger click');
1433         
1434         if(this.disabled){
1435             return;
1436         }
1437         
1438         this.page = 0;
1439         this.loadNext = false;
1440         
1441         if(this.isExpanded()){
1442             this.collapse();
1443             if (!this.blockFocus) {
1444                 this.inputEl().focus();
1445             }
1446             
1447         }else {
1448             this.hasFocus = true;
1449             if(this.triggerAction == 'all') {
1450                 this.doQuery(this.allQuery, true);
1451             } else {
1452                 this.doQuery(this.getRawValue());
1453             }
1454             if (!this.blockFocus) {
1455                 this.inputEl().focus();
1456             }
1457         }
1458     },
1459     
1460     onTickableTriggerClick : function(e)
1461     {
1462         if(this.disabled){
1463             return;
1464         }
1465         
1466         this.page = 0;
1467         this.loadNext = false;
1468         this.hasFocus = true;
1469         
1470         if(this.triggerAction == 'all') {
1471             this.doQuery(this.allQuery, true);
1472         } else {
1473             this.doQuery(this.getRawValue());
1474         }
1475     },
1476     
1477     onSearchFieldClick : function(e)
1478     {
1479         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
1480             return;
1481         }
1482         
1483         this.page = 0;
1484         this.loadNext = false;
1485         this.hasFocus = true;
1486         
1487         if(this.triggerAction == 'all') {
1488             this.doQuery(this.allQuery, true);
1489         } else {
1490             this.doQuery(this.getRawValue());
1491         }
1492     },
1493     
1494     listKeyPress : function(e)
1495     {
1496         //Roo.log('listkeypress');
1497         // scroll to first matching element based on key pres..
1498         if (e.isSpecialKey()) {
1499             return false;
1500         }
1501         var k = String.fromCharCode(e.getKey()).toUpperCase();
1502         //Roo.log(k);
1503         var match  = false;
1504         var csel = this.view.getSelectedNodes();
1505         var cselitem = false;
1506         if (csel.length) {
1507             var ix = this.view.indexOf(csel[0]);
1508             cselitem  = this.store.getAt(ix);
1509             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
1510                 cselitem = false;
1511             }
1512             
1513         }
1514         
1515         this.store.each(function(v) { 
1516             if (cselitem) {
1517                 // start at existing selection.
1518                 if (cselitem.id == v.id) {
1519                     cselitem = false;
1520                 }
1521                 return true;
1522             }
1523                 
1524             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
1525                 match = this.store.indexOf(v);
1526                 return false;
1527             }
1528             return true;
1529         }, this);
1530         
1531         if (match === false) {
1532             return true; // no more action?
1533         }
1534         // scroll to?
1535         this.view.select(match);
1536         var sn = Roo.get(this.view.getSelectedNodes()[0])
1537         //sn.scrollIntoView(sn.dom.parentNode, false);
1538     },
1539     
1540     onViewScroll : function(e, t){
1541         
1542         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
1543             return;
1544         }
1545         
1546         this.hasQuery = true;
1547         
1548         this.loading = this.list.select('.loading', true).first();
1549         
1550         if(this.loading === null){
1551             this.list.createChild({
1552                 tag: 'div',
1553                 cls: 'loading select2-more-results select2-active',
1554                 html: 'Loading more results...'
1555             })
1556             
1557             this.loading = this.list.select('.loading', true).first();
1558             
1559             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
1560             
1561             this.loading.hide();
1562         }
1563         
1564         this.loading.show();
1565         
1566         var _combo = this;
1567         
1568         this.page++;
1569         this.loadNext = true;
1570         
1571         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
1572         
1573         return;
1574     },
1575     
1576     addItem : function(o)
1577     {   
1578         var dv = ''; // display value
1579         
1580         if (this.displayField) {
1581             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1582         } else {
1583             // this is an error condition!!!
1584             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1585         }
1586         
1587         if(!dv.length){
1588             return;
1589         }
1590         
1591         var choice = this.choices.createChild({
1592             tag: 'li',
1593             cls: 'select2-search-choice',
1594             cn: [
1595                 {
1596                     tag: 'div',
1597                     html: dv
1598                 },
1599                 {
1600                     tag: 'a',
1601                     href: '#',
1602                     cls: 'select2-search-choice-close',
1603                     tabindex: '-1'
1604                 }
1605             ]
1606             
1607         }, this.searchField);
1608         
1609         var close = choice.select('a.select2-search-choice-close', true).first()
1610         
1611         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
1612         
1613         this.item.push(o);
1614         
1615         this.lastData = o;
1616         
1617         this.syncValue();
1618         
1619         this.inputEl().dom.value = '';
1620         
1621     },
1622     
1623     onRemoveItem : function(e, _self, o)
1624     {
1625         e.preventDefault();
1626         var index = this.item.indexOf(o.data) * 1;
1627         
1628         if( index < 0){
1629             Roo.log('not this item?!');
1630             return;
1631         }
1632         
1633         this.item.splice(index, 1);
1634         o.item.remove();
1635         
1636         this.syncValue();
1637         
1638         this.fireEvent('remove', this, e);
1639         
1640     },
1641     
1642     syncValue : function()
1643     {
1644         if(!this.item.length){
1645             this.clearValue();
1646             return;
1647         }
1648             
1649         var value = [];
1650         var _this = this;
1651         Roo.each(this.item, function(i){
1652             if(_this.valueField){
1653                 value.push(i[_this.valueField]);
1654                 return;
1655             }
1656
1657             value.push(i);
1658         });
1659
1660         this.value = value.join(',');
1661
1662         if(this.hiddenField){
1663             this.hiddenField.dom.value = this.value;
1664         }
1665     },
1666     
1667     clearItem : function()
1668     {
1669         if(!this.multiple){
1670             return;
1671         }
1672         
1673         this.item = [];
1674         
1675         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
1676            c.remove();
1677         });
1678         
1679         this.syncValue();
1680     },
1681     
1682     inputEl: function ()
1683     {
1684         if(this.tickable){
1685             return this.searchField;
1686         }
1687         return this.el.select('input.form-control',true).first();
1688     },
1689     
1690     
1691     onTickableFooterButtonClick : function(e, btn, el)
1692     {
1693         e.preventDefault();
1694         
1695         if(btn && btn.name == 'cancel'){
1696             this.tickItems = Roo.apply([], this.item);
1697             this.collapse();
1698             return;
1699         }
1700         
1701         this.clearItem();
1702         
1703         var _this = this;
1704         
1705         Roo.each(this.tickItems, function(o){
1706             _this.addItem(o);
1707         });
1708         
1709         this.collapse();
1710         
1711     }
1712     
1713     
1714
1715     /** 
1716     * @cfg {Boolean} grow 
1717     * @hide 
1718     */
1719     /** 
1720     * @cfg {Number} growMin 
1721     * @hide 
1722     */
1723     /** 
1724     * @cfg {Number} growMax 
1725     * @hide 
1726     */
1727     /**
1728      * @hide
1729      * @method autoSize
1730      */
1731 });