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