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