Roo/bootstrap/ComboBox.js
[roojs1] / Roo / bootstrap / ComboBox.js
1 /*
2  * - LGPL
3  * * 
4  */
5
6 /**
7  * @class Roo.bootstrap.ComboBox
8  * @extends Roo.bootstrap.TriggerField
9  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10  * @cfg {Boolean} append (true|false) default false
11  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16  * @cfg {Boolean} animate default true
17  * @cfg {Boolean} emptyResultText only for touch device
18  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
19  * @constructor
20  * Create a new ComboBox.
21  * @param {Object} config Configuration options
22  */
23 Roo.bootstrap.ComboBox = function(config){
24     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
25     this.addEvents({
26         /**
27          * @event expand
28          * Fires when the dropdown list is expanded
29              * @param {Roo.bootstrap.ComboBox} combo This combo box
30              */
31         'expand' : true,
32         /**
33          * @event collapse
34          * Fires when the dropdown list is collapsed
35              * @param {Roo.bootstrap.ComboBox} combo This combo box
36              */
37         'collapse' : true,
38         /**
39          * @event beforeselect
40          * Fires before a list item is selected. Return false to cancel the selection.
41              * @param {Roo.bootstrap.ComboBox} combo This combo box
42              * @param {Roo.data.Record} record The data record returned from the underlying store
43              * @param {Number} index The index of the selected item in the dropdown list
44              */
45         'beforeselect' : true,
46         /**
47          * @event select
48          * Fires when a list item is selected
49              * @param {Roo.bootstrap.ComboBox} combo This combo box
50              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
51              * @param {Number} index The index of the selected item in the dropdown list
52              */
53         'select' : true,
54         /**
55          * @event beforequery
56          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
57          * The event object passed has these properties:
58              * @param {Roo.bootstrap.ComboBox} combo This combo box
59              * @param {String} query The query
60              * @param {Boolean} forceAll true to force "all" query
61              * @param {Boolean} cancel true to cancel the query
62              * @param {Object} e The query event object
63              */
64         'beforequery': true,
65          /**
66          * @event add
67          * Fires when the 'add' icon is pressed (add a listener to enable add button)
68              * @param {Roo.bootstrap.ComboBox} combo This combo box
69              */
70         'add' : true,
71         /**
72          * @event edit
73          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
74              * @param {Roo.bootstrap.ComboBox} combo This combo box
75              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
76              */
77         'edit' : true,
78         /**
79          * @event remove
80          * Fires when the remove value from the combobox array
81              * @param {Roo.bootstrap.ComboBox} combo This combo box
82              */
83         'remove' : true,
84         /**
85          * @event specialfilter
86          * Fires when specialfilter
87             * @param {Roo.bootstrap.ComboBox} combo This combo box
88             */
89         'specialfilter' : true,
90         /**
91          * @event tick
92          * Fires when tick the element
93             * @param {Roo.bootstrap.ComboBox} combo This combo box
94             */
95         'tick' : true,
96         /**
97          * @event touchviewdisplay
98          * Fires when touch view require special display (default is using displayField)
99             * @param {Roo.bootstrap.ComboBox} combo This combo box
100             * @param {Object} cfg set html .
101             */
102         'touchviewdisplay' : true
103         
104     });
105     
106     this.item = [];
107     this.tickItems = [];
108     
109     this.selectedIndex = -1;
110     if(this.mode == 'local'){
111         if(config.queryDelay === undefined){
112             this.queryDelay = 10;
113         }
114         if(config.minChars === undefined){
115             this.minChars = 0;
116         }
117     }
118 };
119
120 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
121      
122     /**
123      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
124      * rendering into an Roo.Editor, defaults to false)
125      */
126     /**
127      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
128      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
129      */
130     /**
131      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
132      */
133     /**
134      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
135      * the dropdown list (defaults to undefined, with no header element)
136      */
137
138      /**
139      * @cfg {String/Roo.Template} tpl The template to use to render the output
140      */
141      
142      /**
143      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
144      */
145     listWidth: undefined,
146     /**
147      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
148      * mode = 'remote' or 'text' if mode = 'local')
149      */
150     displayField: undefined,
151     
152     /**
153      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
154      * mode = 'remote' or 'value' if mode = 'local'). 
155      * Note: use of a valueField requires the user make a selection
156      * in order for a value to be mapped.
157      */
158     valueField: undefined,
159     
160     
161     /**
162      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
163      * field's data value (defaults to the underlying DOM element's name)
164      */
165     hiddenName: undefined,
166     /**
167      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
168      */
169     listClass: '',
170     /**
171      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
172      */
173     selectedClass: 'active',
174     
175     /**
176      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
177      */
178     shadow:'sides',
179     /**
180      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
181      * anchor positions (defaults to 'tl-bl')
182      */
183     listAlign: 'tl-bl?',
184     /**
185      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
186      */
187     maxHeight: 300,
188     /**
189      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
190      * query specified by the allQuery config option (defaults to 'query')
191      */
192     triggerAction: 'query',
193     /**
194      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
195      * (defaults to 4, does not apply if editable = false)
196      */
197     minChars : 4,
198     /**
199      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
200      * delay (typeAheadDelay) if it matches a known value (defaults to false)
201      */
202     typeAhead: false,
203     /**
204      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
205      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
206      */
207     queryDelay: 500,
208     /**
209      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
210      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
211      */
212     pageSize: 0,
213     /**
214      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
215      * when editable = true (defaults to false)
216      */
217     selectOnFocus:false,
218     /**
219      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
220      */
221     queryParam: 'query',
222     /**
223      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
224      * when mode = 'remote' (defaults to 'Loading...')
225      */
226     loadingText: 'Loading...',
227     /**
228      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
229      */
230     resizable: false,
231     /**
232      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
233      */
234     handleHeight : 8,
235     /**
236      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
237      * traditional select (defaults to true)
238      */
239     editable: true,
240     /**
241      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
242      */
243     allQuery: '',
244     /**
245      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
246      */
247     mode: 'remote',
248     /**
249      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
250      * listWidth has a higher value)
251      */
252     minListWidth : 70,
253     /**
254      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
255      * allow the user to set arbitrary text into the field (defaults to false)
256      */
257     forceSelection:false,
258     /**
259      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
260      * if typeAhead = true (defaults to 250)
261      */
262     typeAheadDelay : 250,
263     /**
264      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
265      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
266      */
267     valueNotFoundText : undefined,
268     /**
269      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
270      */
271     blockFocus : false,
272     
273     /**
274      * @cfg {Boolean} disableClear Disable showing of clear button.
275      */
276     disableClear : false,
277     /**
278      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
279      */
280     alwaysQuery : false,
281     
282     /**
283      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
284      */
285     multiple : false,
286     
287     /**
288      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
289      */
290     invalidClass : "has-warning",
291     
292     /**
293      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
294      */
295     validClass : "has-success",
296     
297     /**
298      * @cfg {Boolean} specialFilter (true|false) special filter default false
299      */
300     specialFilter : false,
301     
302     /**
303      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
304      */
305     mobileTouchView : true,
306     
307     //private
308     addicon : false,
309     editicon: false,
310     
311     page: 0,
312     hasQuery: false,
313     append: false,
314     loadNext: false,
315     autoFocus : true,
316     tickable : false,
317     btnPosition : 'right',
318     triggerList : true,
319     showToggleBtn : true,
320     animate : true,
321     emptyResultText: 'Empty',
322     triggerText : 'Select',
323     
324     // element that contains real text value.. (when hidden is used..)
325     
326     getAutoCreate : function()
327     {
328         var cfg = false;
329         
330         /*
331          * Touch Devices
332          */
333         
334         if(Roo.isTouch && this.mobileTouchView){
335             cfg = this.getAutoCreateTouchView();
336             return cfg;;
337         }
338         
339         /*
340          *  Normal ComboBox
341          */
342         if(!this.tickable){
343             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
344             return cfg;
345         }
346         
347         /*
348          *  ComboBox with tickable selections
349          */
350              
351         var align = this.labelAlign || this.parentLabelAlign();
352         
353         cfg = {
354             cls : 'form-group roo-combobox-tickable' //input-group
355         };
356         
357         var buttons = {
358             tag : 'div',
359             cls : 'tickable-buttons',
360             cn : [
361                 {
362                     tag : 'button',
363                     type : 'button',
364                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
365                     html : this.triggerText
366                 },
367                 {
368                     tag : 'button',
369                     type : 'button',
370                     name : 'ok',
371                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
372                     html : 'Done'
373                 },
374                 {
375                     tag : 'button',
376                     type : 'button',
377                     name : 'cancel',
378                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
379                     html : 'Cancel'
380                 }
381             ]
382         };
383         
384         if(this.editable){
385             buttons.cn.unshift({
386                 tag: 'input',
387                 cls: 'select2-search-field-input'
388             });
389         }
390         
391         var _this = this;
392         
393         Roo.each(buttons.cn, function(c){
394             if (_this.size) {
395                 c.cls += ' btn-' + _this.size;
396             }
397
398             if (_this.disabled) {
399                 c.disabled = true;
400             }
401         });
402         
403         var box = {
404             tag: 'div',
405             cn: [
406                 {
407                     tag: 'input',
408                     type : 'hidden',
409                     cls: 'form-hidden-field'
410                 },
411                 {
412                     tag: 'ul',
413                     cls: 'select2-choices',
414                     cn:[
415                         {
416                             tag: 'li',
417                             cls: 'select2-search-field',
418                             cn: [
419
420                                 buttons
421                             ]
422                         }
423                     ]
424                 }
425             ]
426         };
427         
428         var combobox = {
429             cls: 'select2-container input-group select2-container-multi',
430             cn: [
431                 box
432 //                {
433 //                    tag: 'ul',
434 //                    cls: 'typeahead typeahead-long dropdown-menu',
435 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
436 //                }
437             ]
438         };
439         
440         if(this.hasFeedback && !this.allowBlank){
441             
442             var feedback = {
443                 tag: 'span',
444                 cls: 'glyphicon form-control-feedback'
445             };
446
447             combobox.cn.push(feedback);
448         }
449         
450         if (align ==='left' && this.fieldLabel.length) {
451             
452                 Roo.log("left and has label");
453                 cfg.cn = [
454                     
455                     {
456                         tag: 'label',
457                         'for' :  id,
458                         cls : 'control-label col-sm-' + this.labelWidth,
459                         html : this.fieldLabel
460                         
461                     },
462                     {
463                         cls : "col-sm-" + (12 - this.labelWidth), 
464                         cn: [
465                             combobox
466                         ]
467                     }
468                     
469                 ];
470         } else if ( this.fieldLabel.length) {
471                 Roo.log(" label");
472                  cfg.cn = [
473                    
474                     {
475                         tag: 'label',
476                         //cls : 'input-group-addon',
477                         html : this.fieldLabel
478                         
479                     },
480                     
481                     combobox
482                     
483                 ];
484
485         } else {
486             
487                 Roo.log(" no label && no align");
488                 cfg = combobox
489                      
490                 
491         }
492          
493         var settings=this;
494         ['xs','sm','md','lg'].map(function(size){
495             if (settings[size]) {
496                 cfg.cls += ' col-' + size + '-' + settings[size];
497             }
498         });
499         
500         return cfg;
501         
502     },
503     
504     _initEventsCalled : false,
505     
506     // private
507     initEvents: function()
508     {
509         
510         if (this._initEventsCalled) { // as we call render... prevent looping...
511             return;
512         }
513         this._initEventsCalled = true;
514         
515         if (!this.store) {
516             throw "can not find store for combo";
517         }
518         
519         this.store = Roo.factory(this.store, Roo.data);
520         
521         // if we are building from html. then this element is so complex, that we can not really
522         // use the rendered HTML.
523         // so we have to trash and replace the previous code.
524         if (Roo.XComponent.build_from_html) {
525             
526             // remove this element....
527             var e = this.el.dom, k=0;
528             while (e ) { e = e.previousSibling;  ++k;}
529
530             this.el.remove();
531             
532             this.el=false;
533             this.rendered = false;
534             
535             this.render(this.parent().getChildContainer(true), k);
536             
537             
538             
539         }
540         
541         
542         /*
543          * Touch Devices
544          */
545         
546         if(Roo.isTouch && this.mobileTouchView){
547             this.initTouchView();
548             return;
549         }
550         
551         if(this.tickable){
552             this.initTickableEvents();
553             return;
554         }
555         
556         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
557         
558         if(this.hiddenName){
559             
560             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
561             
562             this.hiddenField.dom.value =
563                 this.hiddenValue !== undefined ? this.hiddenValue :
564                 this.value !== undefined ? this.value : '';
565
566             // prevent input submission
567             this.el.dom.removeAttribute('name');
568             this.hiddenField.dom.setAttribute('name', this.hiddenName);
569              
570              
571         }
572         //if(Roo.isGecko){
573         //    this.el.dom.setAttribute('autocomplete', 'off');
574         //}
575         
576         var cls = 'x-combo-list';
577         
578         //this.list = new Roo.Layer({
579         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
580         //});
581         
582         var _this = this;
583         
584         (function(){
585             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
586             _this.list.setWidth(lw);
587         }).defer(100);
588         
589         this.list.on('mouseover', this.onViewOver, this);
590         this.list.on('mousemove', this.onViewMove, this);
591         
592         this.list.on('scroll', this.onViewScroll, this);
593         
594         /*
595         this.list.swallowEvent('mousewheel');
596         this.assetHeight = 0;
597
598         if(this.title){
599             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
600             this.assetHeight += this.header.getHeight();
601         }
602
603         this.innerList = this.list.createChild({cls:cls+'-inner'});
604         this.innerList.on('mouseover', this.onViewOver, this);
605         this.innerList.on('mousemove', this.onViewMove, this);
606         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
607         
608         if(this.allowBlank && !this.pageSize && !this.disableClear){
609             this.footer = this.list.createChild({cls:cls+'-ft'});
610             this.pageTb = new Roo.Toolbar(this.footer);
611            
612         }
613         if(this.pageSize){
614             this.footer = this.list.createChild({cls:cls+'-ft'});
615             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
616                     {pageSize: this.pageSize});
617             
618         }
619         
620         if (this.pageTb && this.allowBlank && !this.disableClear) {
621             var _this = this;
622             this.pageTb.add(new Roo.Toolbar.Fill(), {
623                 cls: 'x-btn-icon x-btn-clear',
624                 text: ' ',
625                 handler: function()
626                 {
627                     _this.collapse();
628                     _this.clearValue();
629                     _this.onSelect(false, -1);
630                 }
631             });
632         }
633         if (this.footer) {
634             this.assetHeight += this.footer.getHeight();
635         }
636         */
637             
638         if(!this.tpl){
639             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
640         }
641
642         this.view = new Roo.View(this.list, this.tpl, {
643             singleSelect:true, store: this.store, selectedClass: this.selectedClass
644         });
645         //this.view.wrapEl.setDisplayed(false);
646         this.view.on('click', this.onViewClick, this);
647         
648         
649         
650         this.store.on('beforeload', this.onBeforeLoad, this);
651         this.store.on('load', this.onLoad, this);
652         this.store.on('loadexception', this.onLoadException, this);
653         /*
654         if(this.resizable){
655             this.resizer = new Roo.Resizable(this.list,  {
656                pinned:true, handles:'se'
657             });
658             this.resizer.on('resize', function(r, w, h){
659                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
660                 this.listWidth = w;
661                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
662                 this.restrictHeight();
663             }, this);
664             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
665         }
666         */
667         if(!this.editable){
668             this.editable = true;
669             this.setEditable(false);
670         }
671         
672         /*
673         
674         if (typeof(this.events.add.listeners) != 'undefined') {
675             
676             this.addicon = this.wrap.createChild(
677                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
678        
679             this.addicon.on('click', function(e) {
680                 this.fireEvent('add', this);
681             }, this);
682         }
683         if (typeof(this.events.edit.listeners) != 'undefined') {
684             
685             this.editicon = this.wrap.createChild(
686                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
687             if (this.addicon) {
688                 this.editicon.setStyle('margin-left', '40px');
689             }
690             this.editicon.on('click', function(e) {
691                 
692                 // we fire even  if inothing is selected..
693                 this.fireEvent('edit', this, this.lastData );
694                 
695             }, this);
696         }
697         */
698         
699         this.keyNav = new Roo.KeyNav(this.inputEl(), {
700             "up" : function(e){
701                 this.inKeyMode = true;
702                 this.selectPrev();
703             },
704
705             "down" : function(e){
706                 if(!this.isExpanded()){
707                     this.onTriggerClick();
708                 }else{
709                     this.inKeyMode = true;
710                     this.selectNext();
711                 }
712             },
713
714             "enter" : function(e){
715 //                this.onViewClick();
716                 //return true;
717                 this.collapse();
718                 
719                 if(this.fireEvent("specialkey", this, e)){
720                     this.onViewClick(false);
721                 }
722                 
723                 return true;
724             },
725
726             "esc" : function(e){
727                 this.collapse();
728             },
729
730             "tab" : function(e){
731                 this.collapse();
732                 
733                 if(this.fireEvent("specialkey", this, e)){
734                     this.onViewClick(false);
735                 }
736                 
737                 return true;
738             },
739
740             scope : this,
741
742             doRelay : function(foo, bar, hname){
743                 if(hname == 'down' || this.scope.isExpanded()){
744                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
745                 }
746                 return true;
747             },
748
749             forceKeyDown: true
750         });
751         
752         
753         this.queryDelay = Math.max(this.queryDelay || 10,
754                 this.mode == 'local' ? 10 : 250);
755         
756         
757         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
758         
759         if(this.typeAhead){
760             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
761         }
762         if(this.editable !== false){
763             this.inputEl().on("keyup", this.onKeyUp, this);
764         }
765         if(this.forceSelection){
766             this.inputEl().on('blur', this.doForce, this);
767         }
768         
769         if(this.multiple){
770             this.choices = this.el.select('ul.select2-choices', true).first();
771             this.searchField = this.el.select('ul li.select2-search-field', true).first();
772         }
773     },
774     
775     initTickableEvents: function()
776     {   
777         this.createList();
778         
779         if(this.hiddenName){
780             
781             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
782             
783             this.hiddenField.dom.value =
784                 this.hiddenValue !== undefined ? this.hiddenValue :
785                 this.value !== undefined ? this.value : '';
786
787             // prevent input submission
788             this.el.dom.removeAttribute('name');
789             this.hiddenField.dom.setAttribute('name', this.hiddenName);
790              
791              
792         }
793         
794 //        this.list = this.el.select('ul.dropdown-menu',true).first();
795         
796         this.choices = this.el.select('ul.select2-choices', true).first();
797         this.searchField = this.el.select('ul li.select2-search-field', true).first();
798         if(this.triggerList){
799             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
800         }
801          
802         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
803         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
804         
805         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
806         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
807         
808         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
809         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
810         
811         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
812         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
813         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
814         
815         this.okBtn.hide();
816         this.cancelBtn.hide();
817         
818         var _this = this;
819         
820         (function(){
821             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
822             _this.list.setWidth(lw);
823         }).defer(100);
824         
825         this.list.on('mouseover', this.onViewOver, this);
826         this.list.on('mousemove', this.onViewMove, this);
827         
828         this.list.on('scroll', this.onViewScroll, this);
829         
830         if(!this.tpl){
831             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>';
832         }
833
834         this.view = new Roo.View(this.list, this.tpl, {
835             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
836         });
837         
838         //this.view.wrapEl.setDisplayed(false);
839         this.view.on('click', this.onViewClick, this);
840         
841         
842         
843         this.store.on('beforeload', this.onBeforeLoad, this);
844         this.store.on('load', this.onLoad, this);
845         this.store.on('loadexception', this.onLoadException, this);
846         
847         if(this.editable){
848             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
849                 "up" : function(e){
850                     this.inKeyMode = true;
851                     this.selectPrev();
852                 },
853
854                 "down" : function(e){
855                     this.inKeyMode = true;
856                     this.selectNext();
857                 },
858
859                 "enter" : function(e){
860                     if(this.fireEvent("specialkey", this, e)){
861                         this.onViewClick(false);
862                     }
863                     
864                     return true;
865                 },
866
867                 "esc" : function(e){
868                     this.onTickableFooterButtonClick(e, false, false);
869                 },
870
871                 "tab" : function(e){
872                     this.fireEvent("specialkey", this, e);
873                     
874                     this.onTickableFooterButtonClick(e, false, false);
875                     
876                     return true;
877                 },
878
879                 scope : this,
880
881                 doRelay : function(e, fn, key){
882                     if(this.scope.isExpanded()){
883                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
884                     }
885                     return true;
886                 },
887
888                 forceKeyDown: true
889             });
890         }
891         
892         this.queryDelay = Math.max(this.queryDelay || 10,
893                 this.mode == 'local' ? 10 : 250);
894         
895         
896         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
897         
898         if(this.typeAhead){
899             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
900         }
901         
902         if(this.editable !== false){
903             this.tickableInputEl().on("keyup", this.onKeyUp, this);
904         }
905         
906     },
907
908     onDestroy : function(){
909         if(this.view){
910             this.view.setStore(null);
911             this.view.el.removeAllListeners();
912             this.view.el.remove();
913             this.view.purgeListeners();
914         }
915         if(this.list){
916             this.list.dom.innerHTML  = '';
917         }
918         
919         if(this.store){
920             this.store.un('beforeload', this.onBeforeLoad, this);
921             this.store.un('load', this.onLoad, this);
922             this.store.un('loadexception', this.onLoadException, this);
923         }
924         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
925     },
926
927     // private
928     fireKey : function(e){
929         if(e.isNavKeyPress() && !this.list.isVisible()){
930             this.fireEvent("specialkey", this, e);
931         }
932     },
933
934     // private
935     onResize: function(w, h){
936 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
937 //        
938 //        if(typeof w != 'number'){
939 //            // we do not handle it!?!?
940 //            return;
941 //        }
942 //        var tw = this.trigger.getWidth();
943 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
944 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
945 //        var x = w - tw;
946 //        this.inputEl().setWidth( this.adjustWidth('input', x));
947 //            
948 //        //this.trigger.setStyle('left', x+'px');
949 //        
950 //        if(this.list && this.listWidth === undefined){
951 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
952 //            this.list.setWidth(lw);
953 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
954 //        }
955         
956     
957         
958     },
959
960     /**
961      * Allow or prevent the user from directly editing the field text.  If false is passed,
962      * the user will only be able to select from the items defined in the dropdown list.  This method
963      * is the runtime equivalent of setting the 'editable' config option at config time.
964      * @param {Boolean} value True to allow the user to directly edit the field text
965      */
966     setEditable : function(value){
967         if(value == this.editable){
968             return;
969         }
970         this.editable = value;
971         if(!value){
972             this.inputEl().dom.setAttribute('readOnly', true);
973             this.inputEl().on('mousedown', this.onTriggerClick,  this);
974             this.inputEl().addClass('x-combo-noedit');
975         }else{
976             this.inputEl().dom.setAttribute('readOnly', false);
977             this.inputEl().un('mousedown', this.onTriggerClick,  this);
978             this.inputEl().removeClass('x-combo-noedit');
979         }
980     },
981
982     // private
983     
984     onBeforeLoad : function(combo,opts){
985         if(!this.hasFocus){
986             return;
987         }
988          if (!opts.add) {
989             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
990          }
991         this.restrictHeight();
992         this.selectedIndex = -1;
993     },
994
995     // private
996     onLoad : function(){
997         
998         this.hasQuery = false;
999         
1000         if(!this.hasFocus){
1001             return;
1002         }
1003         
1004         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
1005             this.loading.hide();
1006         }
1007              
1008         if(this.store.getCount() > 0){
1009             this.expand();
1010             this.restrictHeight();
1011             if(this.lastQuery == this.allQuery){
1012                 if(this.editable && !this.tickable){
1013                     this.inputEl().dom.select();
1014                 }
1015                 
1016                 if(
1017                     !this.selectByValue(this.value, true) &&
1018                     this.autoFocus && 
1019                     (
1020                         !this.store.lastOptions ||
1021                         typeof(this.store.lastOptions.add) == 'undefined' || 
1022                         this.store.lastOptions.add != true
1023                     )
1024                 ){
1025                     this.select(0, true);
1026                 }
1027             }else{
1028                 if(this.autoFocus){
1029                     this.selectNext();
1030                 }
1031                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
1032                     this.taTask.delay(this.typeAheadDelay);
1033                 }
1034             }
1035         }else{
1036             this.onEmptyResults();
1037         }
1038         
1039         //this.el.focus();
1040     },
1041     // private
1042     onLoadException : function()
1043     {
1044         this.hasQuery = false;
1045         
1046         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
1047             this.loading.hide();
1048         }
1049         
1050         if(this.tickable && this.editable){
1051             return;
1052         }
1053         
1054         this.collapse();
1055         // only causes errors at present
1056         //Roo.log(this.store.reader.jsonData);
1057         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
1058             // fixme
1059             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
1060         //}
1061         
1062         
1063     },
1064     // private
1065     onTypeAhead : function(){
1066         if(this.store.getCount() > 0){
1067             var r = this.store.getAt(0);
1068             var newValue = r.data[this.displayField];
1069             var len = newValue.length;
1070             var selStart = this.getRawValue().length;
1071             
1072             if(selStart != len){
1073                 this.setRawValue(newValue);
1074                 this.selectText(selStart, newValue.length);
1075             }
1076         }
1077     },
1078
1079     // private
1080     onSelect : function(record, index){
1081         
1082         if(this.fireEvent('beforeselect', this, record, index) !== false){
1083         
1084             this.setFromData(index > -1 ? record.data : false);
1085             
1086             this.collapse();
1087             this.fireEvent('select', this, record, index);
1088         }
1089     },
1090
1091     /**
1092      * Returns the currently selected field value or empty string if no value is set.
1093      * @return {String} value The selected value
1094      */
1095     getValue : function(){
1096         
1097         if(this.multiple){
1098             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
1099         }
1100         
1101         if(this.valueField){
1102             return typeof this.value != 'undefined' ? this.value : '';
1103         }else{
1104             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
1105         }
1106     },
1107
1108     /**
1109      * Clears any text/value currently set in the field
1110      */
1111     clearValue : function(){
1112         if(this.hiddenField){
1113             this.hiddenField.dom.value = '';
1114         }
1115         this.value = '';
1116         this.setRawValue('');
1117         this.lastSelectionText = '';
1118         this.lastData = false;
1119         
1120         var close = this.closeTriggerEl();
1121         
1122         if(close){
1123             close.hide();
1124         }
1125         
1126     },
1127
1128     /**
1129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
1130      * will be displayed in the field.  If the value does not match the data value of an existing item,
1131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
1132      * Otherwise the field will be blank (although the value will still be set).
1133      * @param {String} value The value to match
1134      */
1135     setValue : function(v){
1136         if(this.multiple){
1137             this.syncValue();
1138             return;
1139         }
1140         
1141         var text = v;
1142         if(this.valueField){
1143             var r = this.findRecord(this.valueField, v);
1144             if(r){
1145                 text = r.data[this.displayField];
1146             }else if(this.valueNotFoundText !== undefined){
1147                 text = this.valueNotFoundText;
1148             }
1149         }
1150         this.lastSelectionText = text;
1151         if(this.hiddenField){
1152             this.hiddenField.dom.value = v;
1153         }
1154         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
1155         this.value = v;
1156         
1157         var close = this.closeTriggerEl();
1158         
1159         if(close){
1160             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
1161         }
1162     },
1163     /**
1164      * @property {Object} the last set data for the element
1165      */
1166     
1167     lastData : false,
1168     /**
1169      * Sets the value of the field based on a object which is related to the record format for the store.
1170      * @param {Object} value the value to set as. or false on reset?
1171      */
1172     setFromData : function(o){
1173         
1174         if(this.multiple){
1175             this.addItem(o);
1176             return;
1177         }
1178             
1179         var dv = ''; // display value
1180         var vv = ''; // value value..
1181         this.lastData = o;
1182         if (this.displayField) {
1183             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1184         } else {
1185             // this is an error condition!!!
1186             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1187         }
1188         
1189         if(this.valueField){
1190             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
1191         }
1192         
1193         var close = this.closeTriggerEl();
1194         
1195         if(close){
1196             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
1197         }
1198         
1199         if(this.hiddenField){
1200             this.hiddenField.dom.value = vv;
1201             
1202             this.lastSelectionText = dv;
1203             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1204             this.value = vv;
1205             return;
1206         }
1207         // no hidden field.. - we store the value in 'value', but still display
1208         // display field!!!!
1209         this.lastSelectionText = dv;
1210         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1211         this.value = vv;
1212         
1213         
1214         
1215     },
1216     // private
1217     reset : function(){
1218         // overridden so that last data is reset..
1219         
1220         if(this.multiple){
1221             this.clearItem();
1222             return;
1223         }
1224         
1225         this.setValue(this.originalValue);
1226         this.clearInvalid();
1227         this.lastData = false;
1228         if (this.view) {
1229             this.view.clearSelections();
1230         }
1231     },
1232     // private
1233     findRecord : function(prop, value){
1234         var record;
1235         if(this.store.getCount() > 0){
1236             this.store.each(function(r){
1237                 if(r.data[prop] == value){
1238                     record = r;
1239                     return false;
1240                 }
1241                 return true;
1242             });
1243         }
1244         return record;
1245     },
1246     
1247     getName: function()
1248     {
1249         // returns hidden if it's set..
1250         if (!this.rendered) {return ''};
1251         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
1252         
1253     },
1254     // private
1255     onViewMove : function(e, t){
1256         this.inKeyMode = false;
1257     },
1258
1259     // private
1260     onViewOver : function(e, t){
1261         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
1262             return;
1263         }
1264         var item = this.view.findItemFromChild(t);
1265         
1266         if(item){
1267             var index = this.view.indexOf(item);
1268             this.select(index, false);
1269         }
1270     },
1271
1272     // private
1273     onViewClick : function(view, doFocus, el, e)
1274     {
1275         var index = this.view.getSelectedIndexes()[0];
1276         
1277         var r = this.store.getAt(index);
1278         
1279         if(this.tickable){
1280             
1281             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
1282                 return;
1283             }
1284             
1285             var rm = false;
1286             var _this = this;
1287             
1288             Roo.each(this.tickItems, function(v,k){
1289                 
1290                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
1291                     Roo.log(v);
1292                     _this.tickItems.splice(k, 1);
1293                     
1294                     if(typeof(e) == 'undefined' && view == false){
1295                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
1296                     }
1297                     
1298                     rm = true;
1299                     return;
1300                 }
1301             });
1302             
1303             if(rm){
1304                 return;
1305             }
1306             
1307             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
1308                 this.tickItems.push(r.data);
1309             }
1310             
1311             if(typeof(e) == 'undefined' && view == false){
1312                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
1313             }
1314                     
1315             return;
1316         }
1317         
1318         if(r){
1319             this.onSelect(r, index);
1320         }
1321         if(doFocus !== false && !this.blockFocus){
1322             this.inputEl().focus();
1323         }
1324     },
1325
1326     // private
1327     restrictHeight : function(){
1328         //this.innerList.dom.style.height = '';
1329         //var inner = this.innerList.dom;
1330         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
1331         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
1332         //this.list.beginUpdate();
1333         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
1334         this.list.alignTo(this.inputEl(), this.listAlign);
1335         this.list.alignTo(this.inputEl(), this.listAlign);
1336         //this.list.endUpdate();
1337     },
1338
1339     // private
1340     onEmptyResults : function(){
1341         
1342         if(this.tickable && this.editable){
1343             this.restrictHeight();
1344             return;
1345         }
1346         
1347         this.collapse();
1348     },
1349
1350     /**
1351      * Returns true if the dropdown list is expanded, else false.
1352      */
1353     isExpanded : function(){
1354         return this.list.isVisible();
1355     },
1356
1357     /**
1358      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
1359      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1360      * @param {String} value The data value of the item to select
1361      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1362      * selected item if it is not currently in view (defaults to true)
1363      * @return {Boolean} True if the value matched an item in the list, else false
1364      */
1365     selectByValue : function(v, scrollIntoView){
1366         if(v !== undefined && v !== null){
1367             var r = this.findRecord(this.valueField || this.displayField, v);
1368             if(r){
1369                 this.select(this.store.indexOf(r), scrollIntoView);
1370                 return true;
1371             }
1372         }
1373         return false;
1374     },
1375
1376     /**
1377      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
1378      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1379      * @param {Number} index The zero-based index of the list item to select
1380      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1381      * selected item if it is not currently in view (defaults to true)
1382      */
1383     select : function(index, scrollIntoView){
1384         this.selectedIndex = index;
1385         this.view.select(index);
1386         if(scrollIntoView !== false){
1387             var el = this.view.getNode(index);
1388             /*
1389              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
1390              */
1391             if(el){
1392                 this.list.scrollChildIntoView(el, false);
1393             }
1394         }
1395     },
1396
1397     // private
1398     selectNext : function(){
1399         var ct = this.store.getCount();
1400         if(ct > 0){
1401             if(this.selectedIndex == -1){
1402                 this.select(0);
1403             }else if(this.selectedIndex < ct-1){
1404                 this.select(this.selectedIndex+1);
1405             }
1406         }
1407     },
1408
1409     // private
1410     selectPrev : function(){
1411         var ct = this.store.getCount();
1412         if(ct > 0){
1413             if(this.selectedIndex == -1){
1414                 this.select(0);
1415             }else if(this.selectedIndex != 0){
1416                 this.select(this.selectedIndex-1);
1417             }
1418         }
1419     },
1420
1421     // private
1422     onKeyUp : function(e){
1423         if(this.editable !== false && !e.isSpecialKey()){
1424             this.lastKey = e.getKey();
1425             this.dqTask.delay(this.queryDelay);
1426         }
1427     },
1428
1429     // private
1430     validateBlur : function(){
1431         return !this.list || !this.list.isVisible();   
1432     },
1433
1434     // private
1435     initQuery : function(){
1436         
1437         var v = this.getRawValue();
1438         
1439         if(this.tickable && this.editable){
1440             v = this.tickableInputEl().getValue();
1441         }
1442         
1443         this.doQuery(v);
1444     },
1445
1446     // private
1447     doForce : function(){
1448         if(this.inputEl().dom.value.length > 0){
1449             this.inputEl().dom.value =
1450                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
1451              
1452         }
1453     },
1454
1455     /**
1456      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
1457      * query allowing the query action to be canceled if needed.
1458      * @param {String} query The SQL query to execute
1459      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
1460      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
1461      * saved in the current store (defaults to false)
1462      */
1463     doQuery : function(q, forceAll){
1464         
1465         if(q === undefined || q === null){
1466             q = '';
1467         }
1468         var qe = {
1469             query: q,
1470             forceAll: forceAll,
1471             combo: this,
1472             cancel:false
1473         };
1474         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
1475             return false;
1476         }
1477         q = qe.query;
1478         
1479         forceAll = qe.forceAll;
1480         if(forceAll === true || (q.length >= this.minChars)){
1481             
1482             this.hasQuery = true;
1483             
1484             if(this.lastQuery != q || this.alwaysQuery){
1485                 this.lastQuery = q;
1486                 if(this.mode == 'local'){
1487                     this.selectedIndex = -1;
1488                     if(forceAll){
1489                         this.store.clearFilter();
1490                     }else{
1491                         
1492                         if(this.specialFilter){
1493                             this.fireEvent('specialfilter', this);
1494                             this.onLoad();
1495                             return;
1496                         }
1497                         
1498                         this.store.filter(this.displayField, q);
1499                     }
1500                     
1501                     this.store.fireEvent("datachanged", this.store);
1502                     
1503                     this.onLoad();
1504                     
1505                     
1506                 }else{
1507                     
1508                     this.store.baseParams[this.queryParam] = q;
1509                     
1510                     var options = {params : this.getParams(q)};
1511                     
1512                     if(this.loadNext){
1513                         options.add = true;
1514                         options.params.start = this.page * this.pageSize;
1515                     }
1516                     
1517                     this.store.load(options);
1518                     
1519                     /*
1520                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
1521                      *  we should expand the list on onLoad
1522                      *  so command out it
1523                      */
1524 //                    this.expand();
1525                 }
1526             }else{
1527                 this.selectedIndex = -1;
1528                 this.onLoad();   
1529             }
1530         }
1531         
1532         this.loadNext = false;
1533     },
1534     
1535     // private
1536     getParams : function(q){
1537         var p = {};
1538         //p[this.queryParam] = q;
1539         
1540         if(this.pageSize){
1541             p.start = 0;
1542             p.limit = this.pageSize;
1543         }
1544         return p;
1545     },
1546
1547     /**
1548      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
1549      */
1550     collapse : function(){
1551         if(!this.isExpanded()){
1552             return;
1553         }
1554         
1555         this.list.hide();
1556         
1557         if(this.tickable){
1558             this.hasFocus = false;
1559             this.okBtn.hide();
1560             this.cancelBtn.hide();
1561             this.trigger.show();
1562             
1563             if(this.editable){
1564                 this.tickableInputEl().dom.value = '';
1565                 this.tickableInputEl().blur();
1566             }
1567             
1568         }
1569         
1570         Roo.get(document).un('mousedown', this.collapseIf, this);
1571         Roo.get(document).un('mousewheel', this.collapseIf, this);
1572         if (!this.editable) {
1573             Roo.get(document).un('keydown', this.listKeyPress, this);
1574         }
1575         this.fireEvent('collapse', this);
1576     },
1577
1578     // private
1579     collapseIf : function(e){
1580         var in_combo  = e.within(this.el);
1581         var in_list =  e.within(this.list);
1582         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
1583         
1584         if (in_combo || in_list || is_list) {
1585             //e.stopPropagation();
1586             return;
1587         }
1588         
1589         if(this.tickable){
1590             this.onTickableFooterButtonClick(e, false, false);
1591         }
1592
1593         this.collapse();
1594         
1595     },
1596
1597     /**
1598      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
1599      */
1600     expand : function(){
1601        
1602         if(this.isExpanded() || !this.hasFocus){
1603             return;
1604         }
1605         
1606         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
1607         this.list.setWidth(lw);
1608         
1609         
1610          Roo.log('expand');
1611         
1612         this.list.show();
1613         
1614         this.restrictHeight();
1615         
1616         if(this.tickable){
1617             
1618             this.tickItems = Roo.apply([], this.item);
1619             
1620             this.okBtn.show();
1621             this.cancelBtn.show();
1622             this.trigger.hide();
1623             
1624             if(this.editable){
1625                 this.tickableInputEl().focus();
1626             }
1627             
1628         }
1629         
1630         Roo.get(document).on('mousedown', this.collapseIf, this);
1631         Roo.get(document).on('mousewheel', this.collapseIf, this);
1632         if (!this.editable) {
1633             Roo.get(document).on('keydown', this.listKeyPress, this);
1634         }
1635         
1636         this.fireEvent('expand', this);
1637     },
1638
1639     // private
1640     // Implements the default empty TriggerField.onTriggerClick function
1641     onTriggerClick : function(e)
1642     {
1643         Roo.log('trigger click');
1644         
1645         if(this.disabled || !this.triggerList){
1646             return;
1647         }
1648         
1649         this.page = 0;
1650         this.loadNext = false;
1651         
1652         if(this.isExpanded()){
1653             this.collapse();
1654             if (!this.blockFocus) {
1655                 this.inputEl().focus();
1656             }
1657             
1658         }else {
1659             this.hasFocus = true;
1660             if(this.triggerAction == 'all') {
1661                 this.doQuery(this.allQuery, true);
1662             } else {
1663                 this.doQuery(this.getRawValue());
1664             }
1665             if (!this.blockFocus) {
1666                 this.inputEl().focus();
1667             }
1668         }
1669     },
1670     
1671     onTickableTriggerClick : function(e)
1672     {
1673         if(this.disabled){
1674             return;
1675         }
1676         
1677         this.page = 0;
1678         this.loadNext = false;
1679         this.hasFocus = true;
1680         
1681         if(this.triggerAction == 'all') {
1682             this.doQuery(this.allQuery, true);
1683         } else {
1684             this.doQuery(this.getRawValue());
1685         }
1686     },
1687     
1688     onSearchFieldClick : function(e)
1689     {
1690         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
1691             this.onTickableFooterButtonClick(e, false, false);
1692             return;
1693         }
1694         
1695         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
1696             return;
1697         }
1698         
1699         this.page = 0;
1700         this.loadNext = false;
1701         this.hasFocus = true;
1702         
1703         if(this.triggerAction == 'all') {
1704             this.doQuery(this.allQuery, true);
1705         } else {
1706             this.doQuery(this.getRawValue());
1707         }
1708     },
1709     
1710     listKeyPress : function(e)
1711     {
1712         //Roo.log('listkeypress');
1713         // scroll to first matching element based on key pres..
1714         if (e.isSpecialKey()) {
1715             return false;
1716         }
1717         var k = String.fromCharCode(e.getKey()).toUpperCase();
1718         //Roo.log(k);
1719         var match  = false;
1720         var csel = this.view.getSelectedNodes();
1721         var cselitem = false;
1722         if (csel.length) {
1723             var ix = this.view.indexOf(csel[0]);
1724             cselitem  = this.store.getAt(ix);
1725             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
1726                 cselitem = false;
1727             }
1728             
1729         }
1730         
1731         this.store.each(function(v) { 
1732             if (cselitem) {
1733                 // start at existing selection.
1734                 if (cselitem.id == v.id) {
1735                     cselitem = false;
1736                 }
1737                 return true;
1738             }
1739                 
1740             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
1741                 match = this.store.indexOf(v);
1742                 return false;
1743             }
1744             return true;
1745         }, this);
1746         
1747         if (match === false) {
1748             return true; // no more action?
1749         }
1750         // scroll to?
1751         this.view.select(match);
1752         var sn = Roo.get(this.view.getSelectedNodes()[0]);
1753         sn.scrollIntoView(sn.dom.parentNode, false);
1754     },
1755     
1756     onViewScroll : function(e, t){
1757         
1758         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
1759             return;
1760         }
1761         
1762         this.hasQuery = true;
1763         
1764         this.loading = this.list.select('.loading', true).first();
1765         
1766         if(this.loading === null){
1767             this.list.createChild({
1768                 tag: 'div',
1769                 cls: 'loading select2-more-results select2-active',
1770                 html: 'Loading more results...'
1771             });
1772             
1773             this.loading = this.list.select('.loading', true).first();
1774             
1775             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
1776             
1777             this.loading.hide();
1778         }
1779         
1780         this.loading.show();
1781         
1782         var _combo = this;
1783         
1784         this.page++;
1785         this.loadNext = true;
1786         
1787         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
1788         
1789         return;
1790     },
1791     
1792     addItem : function(o)
1793     {   
1794         var dv = ''; // display value
1795         
1796         if (this.displayField) {
1797             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1798         } else {
1799             // this is an error condition!!!
1800             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1801         }
1802         
1803         if(!dv.length){
1804             return;
1805         }
1806         
1807         var choice = this.choices.createChild({
1808             tag: 'li',
1809             cls: 'select2-search-choice',
1810             cn: [
1811                 {
1812                     tag: 'div',
1813                     html: dv
1814                 },
1815                 {
1816                     tag: 'a',
1817                     href: '#',
1818                     cls: 'select2-search-choice-close',
1819                     tabindex: '-1'
1820                 }
1821             ]
1822             
1823         }, this.searchField);
1824         
1825         var close = choice.select('a.select2-search-choice-close', true).first();
1826         
1827         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
1828         
1829         this.item.push(o);
1830         
1831         this.lastData = o;
1832         
1833         this.syncValue();
1834         
1835         this.inputEl().dom.value = '';
1836         
1837         this.validate();
1838     },
1839     
1840     onRemoveItem : function(e, _self, o)
1841     {
1842         e.preventDefault();
1843         
1844         this.lastItem = Roo.apply([], this.item);
1845         
1846         var index = this.item.indexOf(o.data) * 1;
1847         
1848         if( index < 0){
1849             Roo.log('not this item?!');
1850             return;
1851         }
1852         
1853         this.item.splice(index, 1);
1854         o.item.remove();
1855         
1856         this.syncValue();
1857         
1858         this.fireEvent('remove', this, e);
1859         
1860         this.validate();
1861         
1862     },
1863     
1864     syncValue : function()
1865     {
1866         if(!this.item.length){
1867             this.clearValue();
1868             return;
1869         }
1870             
1871         var value = [];
1872         var _this = this;
1873         Roo.each(this.item, function(i){
1874             if(_this.valueField){
1875                 value.push(i[_this.valueField]);
1876                 return;
1877             }
1878
1879             value.push(i);
1880         });
1881
1882         this.value = value.join(',');
1883
1884         if(this.hiddenField){
1885             this.hiddenField.dom.value = this.value;
1886         }
1887         
1888         this.store.fireEvent("datachanged", this.store);
1889     },
1890     
1891     clearItem : function()
1892     {
1893         if(!this.multiple){
1894             return;
1895         }
1896         
1897         this.item = [];
1898         
1899         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
1900            c.remove();
1901         });
1902         
1903         this.syncValue();
1904         
1905         this.validate();
1906         
1907         if(this.tickable && !Roo.isTouch){
1908             this.view.refresh();
1909         }
1910     },
1911     
1912     inputEl: function ()
1913     {
1914         if(Roo.isTouch && this.mobileTouchView){
1915             return this.el.select('input.form-control',true).first();
1916         }
1917         
1918         if(this.tickable){
1919             return this.searchField;
1920         }
1921         
1922         return this.el.select('input.form-control',true).first();
1923     },
1924     
1925     
1926     onTickableFooterButtonClick : function(e, btn, el)
1927     {
1928         e.preventDefault();
1929         
1930         this.lastItem = Roo.apply([], this.item);
1931         
1932         if(btn && btn.name == 'cancel'){
1933             this.tickItems = Roo.apply([], this.item);
1934             this.collapse();
1935             return;
1936         }
1937         
1938         this.clearItem();
1939         
1940         var _this = this;
1941         
1942         Roo.each(this.tickItems, function(o){
1943             _this.addItem(o);
1944         });
1945         
1946         this.collapse();
1947         
1948     },
1949     
1950     validate : function()
1951     {
1952         var v = this.getRawValue();
1953         
1954         if(this.multiple){
1955             v = this.getValue();
1956         }
1957         
1958         if(this.disabled || this.allowBlank || v.length){
1959             this.markValid();
1960             return true;
1961         }
1962         
1963         this.markInvalid();
1964         return false;
1965     },
1966     
1967     tickableInputEl : function()
1968     {
1969         if(!this.tickable || !this.editable){
1970             return this.inputEl();
1971         }
1972         
1973         return this.inputEl().select('.select2-search-field-input', true).first();
1974     },
1975     
1976     
1977     getAutoCreateTouchView : function()
1978     {
1979         var id = Roo.id();
1980         
1981         var cfg = {
1982             cls: 'form-group' //input-group
1983         };
1984         
1985         var input =  {
1986             tag: 'input',
1987             id : id,
1988             type : this.inputType,
1989             cls : 'form-control x-combo-noedit',
1990             autocomplete: 'new-password',
1991             placeholder : this.placeholder || '',
1992             readonly : true
1993         };
1994         
1995         if (this.name) {
1996             input.name = this.name;
1997         }
1998         
1999         if (this.size) {
2000             input.cls += ' input-' + this.size;
2001         }
2002         
2003         if (this.disabled) {
2004             input.disabled = true;
2005         }
2006         
2007         var inputblock = {
2008             cls : '',
2009             cn : [
2010                 input
2011             ]
2012         };
2013         
2014         if(this.before){
2015             inputblock.cls += ' input-group';
2016             
2017             inputblock.cn.unshift({
2018                 tag :'span',
2019                 cls : 'input-group-addon',
2020                 html : this.before
2021             });
2022         }
2023         
2024         if(this.removable && !this.multiple){
2025             inputblock.cls += ' roo-removable';
2026             
2027             inputblock.cn.push({
2028                 tag: 'button',
2029                 html : 'x',
2030                 cls : 'roo-combo-removable-btn close'
2031             });
2032         }
2033
2034         if(this.hasFeedback && !this.allowBlank){
2035             
2036             inputblock.cls += ' has-feedback';
2037             
2038             inputblock.cn.push({
2039                 tag: 'span',
2040                 cls: 'glyphicon form-control-feedback'
2041             });
2042             
2043         }
2044         
2045         if (this.after) {
2046             
2047             inputblock.cls += (this.before) ? '' : ' input-group';
2048             
2049             inputblock.cn.push({
2050                 tag :'span',
2051                 cls : 'input-group-addon',
2052                 html : this.after
2053             });
2054         }
2055
2056         var box = {
2057             tag: 'div',
2058             cn: [
2059                 {
2060                     tag: 'input',
2061                     type : 'hidden',
2062                     cls: 'form-hidden-field'
2063                 },
2064                 inputblock
2065             ]
2066             
2067         };
2068         
2069         if(this.multiple){
2070             box = {
2071                 tag: 'div',
2072                 cn: [
2073                     {
2074                         tag: 'input',
2075                         type : 'hidden',
2076                         cls: 'form-hidden-field'
2077                     },
2078                     {
2079                         tag: 'ul',
2080                         cls: 'select2-choices',
2081                         cn:[
2082                             {
2083                                 tag: 'li',
2084                                 cls: 'select2-search-field',
2085                                 cn: [
2086
2087                                     inputblock
2088                                 ]
2089                             }
2090                         ]
2091                     }
2092                 ]
2093             }
2094         };
2095         
2096         var combobox = {
2097             cls: 'select2-container input-group',
2098             cn: [
2099                 box
2100             ]
2101         };
2102         
2103         if(this.multiple){
2104             combobox.cls += ' select2-container-multi';
2105         }
2106         
2107         var align = this.labelAlign || this.parentLabelAlign();
2108         
2109         cfg.cn = combobox;
2110         
2111         if(this.fieldLabel.length){
2112             
2113             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
2114             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
2115             
2116             cfg.cn = [
2117                 {
2118                     tag: 'label',
2119                     cls : 'control-label ' + lw,
2120                     html : this.fieldLabel
2121
2122                 },
2123                 {
2124                     cls : cw, 
2125                     cn: [
2126                         combobox
2127                     ]
2128                 }
2129             ];
2130         }
2131         
2132         var settings = this;
2133         
2134         ['xs','sm','md','lg'].map(function(size){
2135             if (settings[size]) {
2136                 cfg.cls += ' col-' + size + '-' + settings[size];
2137             }
2138         });
2139         
2140         return cfg;
2141     },
2142     
2143     initTouchView : function()
2144     {
2145         this.renderTouchView();
2146         
2147         this.touchViewEl.on('scroll', function(){
2148             this.el.dom.scrollTop = 0;
2149         }, this);
2150         
2151         this.inputEl().on("click", this.showTouchView, this);
2152         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
2153         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
2154         
2155         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
2156         
2157         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
2158         this.store.on('load', this.onTouchViewLoad, this);
2159         this.store.on('loadexception', this.onTouchViewLoadException, this);
2160         
2161         if(this.hiddenName){
2162             
2163             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
2164             
2165             this.hiddenField.dom.value =
2166                 this.hiddenValue !== undefined ? this.hiddenValue :
2167                 this.value !== undefined ? this.value : '';
2168         
2169             this.el.dom.removeAttribute('name');
2170             this.hiddenField.dom.setAttribute('name', this.hiddenName);
2171         }
2172         
2173         if(this.multiple){
2174             this.choices = this.el.select('ul.select2-choices', true).first();
2175             this.searchField = this.el.select('ul li.select2-search-field', true).first();
2176         }
2177         
2178         if(this.removable && !this.multiple){
2179             var close = this.closeTriggerEl();
2180             if(close){
2181                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
2182                 close.on('click', this.removeBtnClick, this, close);
2183             }
2184         }
2185         
2186         return;
2187         
2188         
2189     },
2190     
2191     renderTouchView : function()
2192     {
2193         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
2194         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
2195         
2196         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
2197         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
2198         
2199         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
2200         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
2201         this.touchViewBodyEl.setStyle('overflow', 'auto');
2202         
2203         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
2204         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
2205         
2206         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
2207         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
2208         
2209     },
2210     
2211     showTouchView : function()
2212     {
2213         this.touchViewHeaderEl.hide();
2214
2215         if(this.fieldLabel.length){
2216             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
2217             this.touchViewHeaderEl.show();
2218         }
2219
2220         this.touchViewEl.show();
2221
2222         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
2223         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2224
2225         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
2226
2227         if(this.fieldLabel.length){
2228             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
2229         }
2230         
2231         this.touchViewBodyEl.setHeight(bodyHeight);
2232
2233         if(this.animate){
2234             var _this = this;
2235             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
2236         }else{
2237             this.touchViewEl.addClass('in');
2238         }
2239
2240         this.doTouchViewQuery();
2241         
2242     },
2243     
2244     hideTouchView : function()
2245     {
2246         this.touchViewEl.removeClass('in');
2247
2248         if(this.animate){
2249             var _this = this;
2250             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
2251         }else{
2252             this.touchViewEl.setStyle('display', 'none');
2253         }
2254         
2255     },
2256     
2257     setTouchViewValue : function()
2258     {
2259         if(this.multiple){
2260             this.clearItem();
2261         
2262             var _this = this;
2263
2264             Roo.each(this.tickItems, function(o){
2265                 this.addItem(o);
2266             }, this);
2267         }
2268         
2269         this.hideTouchView();
2270     },
2271     
2272     doTouchViewQuery : function()
2273     {
2274         var qe = {
2275             query: '',
2276             forceAll: true,
2277             combo: this,
2278             cancel:false
2279         };
2280         
2281         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
2282             return false;
2283         }
2284         
2285         if(!this.alwaysQuery || this.mode == 'local'){
2286             this.onTouchViewLoad();
2287             return;
2288         }
2289         
2290         this.store.load();
2291     },
2292     
2293     onTouchViewBeforeLoad : function(combo,opts)
2294     {
2295         return;
2296     },
2297
2298     // private
2299     onTouchViewLoad : function()
2300     {
2301         if(this.store.getCount() < 1){
2302             this.onTouchViewEmptyResults();
2303             return;
2304         }
2305         
2306         this.clearTouchView();
2307         
2308         var rawValue = this.getRawValue();
2309         
2310         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
2311         
2312         this.tickItems = [];
2313         
2314         this.store.data.each(function(d, rowIndex){
2315             var row = this.touchViewListGroup.createChild(template);
2316             
2317             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
2318                 var cfg = {
2319                     html : d.data[this.displayField]
2320                 }
2321                 
2322                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
2323                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
2324                 }
2325             }
2326             
2327             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
2328                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
2329             }
2330             
2331             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
2332                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
2333                 this.tickItems.push(d.data);
2334             }
2335             
2336             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
2337             
2338         }, this);
2339         
2340         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
2341         
2342         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
2343
2344         if(this.fieldLabel.length){
2345             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
2346         }
2347
2348         var listHeight = this.touchViewListGroup.getHeight();
2349         
2350         var _this = this;
2351         
2352         if(firstChecked && listHeight > bodyHeight){
2353             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
2354         }
2355         
2356     },
2357     
2358     onTouchViewLoadException : function()
2359     {
2360         this.hideTouchView();
2361     },
2362     
2363     onTouchViewEmptyResults : function()
2364     {
2365         this.clearTouchView();
2366         
2367         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
2368         
2369         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
2370         
2371     },
2372     
2373     clearTouchView : function()
2374     {
2375         this.touchViewListGroup.dom.innerHTML = '';
2376     },
2377     
2378     onTouchViewClick : function(e, el, o)
2379     {
2380         e.preventDefault();
2381         
2382         var row = o.row;
2383         var rowIndex = o.rowIndex;
2384         
2385         var r = this.store.getAt(rowIndex);
2386         
2387         if(!this.multiple){
2388             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
2389                 c.dom.removeAttribute('checked');
2390             }, this);
2391             
2392             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
2393         
2394             this.setFromData(r.data);
2395             
2396             var close = this.closeTriggerEl();
2397         
2398             if(close){
2399                 close.show();
2400             }
2401
2402             this.hideTouchView();
2403             
2404             this.fireEvent('select', this, r, rowIndex);
2405             
2406             return;
2407         }
2408         
2409         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
2410             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
2411             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
2412             return;
2413         }
2414         
2415         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
2416         this.addItem(r.data);
2417         this.tickItems.push(r.data);
2418         
2419     }
2420     
2421
2422     /** 
2423     * @cfg {Boolean} grow 
2424     * @hide 
2425     */
2426     /** 
2427     * @cfg {Number} growMin 
2428     * @hide 
2429     */
2430     /** 
2431     * @cfg {Number} growMax 
2432     * @hide 
2433     */
2434     /**
2435      * @hide
2436      * @method autoSize
2437      */
2438 });
2439
2440 Roo.apply(Roo.bootstrap.ComboBox,  {
2441     
2442     header : {
2443         tag: 'div',
2444         cls: 'modal-header',
2445         cn: [
2446             {
2447                 tag: 'h4',
2448                 cls: 'modal-title'
2449             }
2450         ]
2451     },
2452     
2453     body : {
2454         tag: 'div',
2455         cls: 'modal-body',
2456         cn: [
2457             {
2458                 tag: 'ul',
2459                 cls: 'list-group'
2460             }
2461         ]
2462     },
2463     
2464     listItemRadio : {
2465         tag: 'li',
2466         cls: 'list-group-item',
2467         cn: [
2468             {
2469                 tag: 'span',
2470                 cls: 'roo-combobox-list-group-item-value'
2471             },
2472             {
2473                 tag: 'div',
2474                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
2475                 cn: [
2476                     {
2477                         tag: 'input',
2478                         type: 'radio'
2479                     },
2480                     {
2481                         tag: 'label'
2482                     }
2483                 ]
2484             }
2485         ]
2486     },
2487     
2488     listItemCheckbox : {
2489         tag: 'li',
2490         cls: 'list-group-item',
2491         cn: [
2492             {
2493                 tag: 'span',
2494                 cls: 'roo-combobox-list-group-item-value'
2495             },
2496             {
2497                 tag: 'div',
2498                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
2499                 cn: [
2500                     {
2501                         tag: 'input',
2502                         type: 'checkbox'
2503                     },
2504                     {
2505                         tag: 'label'
2506                     }
2507                 ]
2508             }
2509         ]
2510     },
2511     
2512     emptyResult : {
2513         tag: 'div',
2514         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
2515     },
2516     
2517     footer : {
2518         tag: 'div',
2519         cls: 'modal-footer',
2520         cn: [
2521             {
2522                 tag: 'div',
2523                 cls: 'row',
2524                 cn: [
2525                     {
2526                         tag: 'div',
2527                         cls: 'col-xs-6 text-left',
2528                         cn: {
2529                             tag: 'button',
2530                             cls: 'btn btn-danger roo-touch-view-cancel',
2531                             html: 'Cancel'
2532                         }
2533                     },
2534                     {
2535                         tag: 'div',
2536                         cls: 'col-xs-6 text-right',
2537                         cn: {
2538                             tag: 'button',
2539                             cls: 'btn btn-success roo-touch-view-ok',
2540                             html: 'OK'
2541                         }
2542                     }
2543                 ]
2544             }
2545         ]
2546         
2547     }
2548 });
2549
2550 Roo.apply(Roo.bootstrap.ComboBox,  {
2551     
2552     touchViewTemplate : {
2553         tag: 'div',
2554         cls: 'modal fade roo-combobox-touch-view',
2555         cn: [
2556             {
2557                 tag: 'div',
2558                 cls: 'modal-dialog',
2559                 cn: [
2560                     {
2561                         tag: 'div',
2562                         cls: 'modal-content',
2563                         cn: [
2564                             Roo.bootstrap.ComboBox.header,
2565                             Roo.bootstrap.ComboBox.body,
2566                             Roo.bootstrap.ComboBox.footer
2567                         ]
2568                     }
2569                 ]
2570             }
2571         ]
2572     }
2573 });