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