roojs-bootstrap.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 {String} btnPosition set the position of the trigger button (left | right) default right
15  * @constructor
16  * Create a new ComboBox.
17  * @param {Object} config Configuration options
18  */
19 Roo.bootstrap.ComboBox = function(config){
20     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
21     this.addEvents({
22         /**
23          * @event expand
24          * Fires when the dropdown list is expanded
25              * @param {Roo.bootstrap.ComboBox} combo This combo box
26              */
27         'expand' : true,
28         /**
29          * @event collapse
30          * Fires when the dropdown list is collapsed
31              * @param {Roo.bootstrap.ComboBox} combo This combo box
32              */
33         'collapse' : true,
34         /**
35          * @event beforeselect
36          * Fires before a list item is selected. Return false to cancel the selection.
37              * @param {Roo.bootstrap.ComboBox} combo This combo box
38              * @param {Roo.data.Record} record The data record returned from the underlying store
39              * @param {Number} index The index of the selected item in the dropdown list
40              */
41         'beforeselect' : true,
42         /**
43          * @event select
44          * Fires when a list item is selected
45              * @param {Roo.bootstrap.ComboBox} combo This combo box
46              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
47              * @param {Number} index The index of the selected item in the dropdown list
48              */
49         'select' : true,
50         /**
51          * @event beforequery
52          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
53          * The event object passed has these properties:
54              * @param {Roo.bootstrap.ComboBox} combo This combo box
55              * @param {String} query The query
56              * @param {Boolean} forceAll true to force "all" query
57              * @param {Boolean} cancel true to cancel the query
58              * @param {Object} e The query event object
59              */
60         'beforequery': true,
61          /**
62          * @event add
63          * Fires when the 'add' icon is pressed (add a listener to enable add button)
64              * @param {Roo.bootstrap.ComboBox} combo This combo box
65              */
66         'add' : true,
67         /**
68          * @event edit
69          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
70              * @param {Roo.bootstrap.ComboBox} combo This combo box
71              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
72              */
73         'edit' : true,
74         /**
75          * @event remove
76          * Fires when the remove value from the combobox array
77              * @param {Roo.bootstrap.ComboBox} combo This combo box
78              */
79         'remove' : true
80         
81     });
82     
83     this.item = [];
84     this.tickItems = [];
85     
86     this.selectedIndex = -1;
87     if(this.mode == 'local'){
88         if(config.queryDelay === undefined){
89             this.queryDelay = 10;
90         }
91         if(config.minChars === undefined){
92             this.minChars = 0;
93         }
94     }
95 };
96
97 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
98      
99     /**
100      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
101      * rendering into an Roo.Editor, defaults to false)
102      */
103     /**
104      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
105      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
106      */
107     /**
108      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
109      */
110     /**
111      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
112      * the dropdown list (defaults to undefined, with no header element)
113      */
114
115      /**
116      * @cfg {String/Roo.Template} tpl The template to use to render the output
117      */
118      
119      /**
120      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
121      */
122     listWidth: undefined,
123     /**
124      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
125      * mode = 'remote' or 'text' if mode = 'local')
126      */
127     displayField: undefined,
128     /**
129      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
130      * mode = 'remote' or 'value' if mode = 'local'). 
131      * Note: use of a valueField requires the user make a selection
132      * in order for a value to be mapped.
133      */
134     valueField: undefined,
135     
136     
137     /**
138      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
139      * field's data value (defaults to the underlying DOM element's name)
140      */
141     hiddenName: undefined,
142     /**
143      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
144      */
145     listClass: '',
146     /**
147      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
148      */
149     selectedClass: 'active',
150     
151     /**
152      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
153      */
154     shadow:'sides',
155     /**
156      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
157      * anchor positions (defaults to 'tl-bl')
158      */
159     listAlign: 'tl-bl?',
160     /**
161      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
162      */
163     maxHeight: 300,
164     /**
165      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
166      * query specified by the allQuery config option (defaults to 'query')
167      */
168     triggerAction: 'query',
169     /**
170      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
171      * (defaults to 4, does not apply if editable = false)
172      */
173     minChars : 4,
174     /**
175      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
176      * delay (typeAheadDelay) if it matches a known value (defaults to false)
177      */
178     typeAhead: false,
179     /**
180      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
181      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
182      */
183     queryDelay: 500,
184     /**
185      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
186      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
187      */
188     pageSize: 0,
189     /**
190      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
191      * when editable = true (defaults to false)
192      */
193     selectOnFocus:false,
194     /**
195      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
196      */
197     queryParam: 'query',
198     /**
199      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
200      * when mode = 'remote' (defaults to 'Loading...')
201      */
202     loadingText: 'Loading...',
203     /**
204      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
205      */
206     resizable: false,
207     /**
208      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
209      */
210     handleHeight : 8,
211     /**
212      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
213      * traditional select (defaults to true)
214      */
215     editable: true,
216     /**
217      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
218      */
219     allQuery: '',
220     /**
221      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
222      */
223     mode: 'remote',
224     /**
225      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
226      * listWidth has a higher value)
227      */
228     minListWidth : 70,
229     /**
230      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
231      * allow the user to set arbitrary text into the field (defaults to false)
232      */
233     forceSelection:false,
234     /**
235      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
236      * if typeAhead = true (defaults to 250)
237      */
238     typeAheadDelay : 250,
239     /**
240      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
241      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
242      */
243     valueNotFoundText : undefined,
244     /**
245      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
246      */
247     blockFocus : false,
248     
249     /**
250      * @cfg {Boolean} disableClear Disable showing of clear button.
251      */
252     disableClear : false,
253     /**
254      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
255      */
256     alwaysQuery : false,
257     
258     /**
259      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
260      */
261     multiple : false,
262     
263     //private
264     addicon : false,
265     editicon: false,
266     
267     page: 0,
268     hasQuery: false,
269     append: false,
270     loadNext: false,
271     autoFocus : true,
272     tickable : false,
273     btnPosition : 'right',
274     triggerList : true,
275     // element that contains real text value.. (when hidden is used..)
276     
277     getAutoCreate : function()
278     {
279         var cfg = false;
280         
281         /*
282          *  Normal ComboBox
283          */
284         if(!this.tickable){
285             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
286             return cfg;
287         }
288         
289         /*
290          *  ComboBox with tickable selections
291          */
292              
293         var align = this.labelAlign || this.parentLabelAlign();
294         
295         cfg = {
296             cls : 'form-group roo-combobox-tickable' //input-group
297         };
298         
299         
300         var buttons = {
301             tag : 'div',
302             cls : 'tickable-buttons',
303             cn : [
304                 {
305                     tag : 'button',
306                     type : 'button',
307                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
308                     html : 'Edit'
309                 },
310                 {
311                     tag : 'button',
312                     type : 'button',
313                     name : 'ok',
314                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
315                     html : 'Done'
316                 },
317                 {
318                     tag : 'button',
319                     type : 'button',
320                     name : 'cancel',
321                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
322                     html : 'Cancel'
323                 }
324             ]
325         };
326         
327         var _this = this;
328         Roo.each(buttons.cn, function(c){
329             if (_this.size) {
330                 c.cls += ' btn-' + _this.size;
331             }
332
333             if (_this.disabled) {
334                 c.disabled = true;
335             }
336         });
337         
338         var box = {
339             tag: 'div',
340             cn: [
341                 {
342                     tag: 'input',
343                     type : 'hidden',
344                     cls: 'form-hidden-field'
345                 },
346                 {
347                     tag: 'ul',
348                     cls: 'select2-choices',
349                     cn:[
350                         {
351                             tag: 'li',
352                             cls: 'select2-search-field',
353                             cn: [
354
355                                 buttons
356                             ]
357                         }
358                     ]
359                 }
360             ]
361         }
362         
363         var combobox = {
364             cls: 'select2-container input-group select2-container-multi',
365             cn: [
366                 box
367 //                {
368 //                    tag: 'ul',
369 //                    cls: 'typeahead typeahead-long dropdown-menu',
370 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
371 //                }
372             ]
373         };
374         
375         if (align ==='left' && this.fieldLabel.length) {
376             
377                 Roo.log("left and has label");
378                 cfg.cn = [
379                     
380                     {
381                         tag: 'label',
382                         'for' :  id,
383                         cls : 'control-label col-sm-' + this.labelWidth,
384                         html : this.fieldLabel
385                         
386                     },
387                     {
388                         cls : "col-sm-" + (12 - this.labelWidth), 
389                         cn: [
390                             combobox
391                         ]
392                     }
393                     
394                 ];
395         } else if ( this.fieldLabel.length) {
396                 Roo.log(" label");
397                  cfg.cn = [
398                    
399                     {
400                         tag: 'label',
401                         //cls : 'input-group-addon',
402                         html : this.fieldLabel
403                         
404                     },
405                     
406                     combobox
407                     
408                 ];
409
410         } else {
411             
412                 Roo.log(" no label && no align");
413                 cfg = combobox
414                      
415                 
416         }
417          
418         var settings=this;
419         ['xs','sm','md','lg'].map(function(size){
420             if (settings[size]) {
421                 cfg.cls += ' col-' + size + '-' + settings[size];
422             }
423         });
424         
425         return cfg;
426         
427     },
428     
429     // private
430     initEvents: function()
431     {
432         
433         if (!this.store) {
434             throw "can not find store for combo";
435         }
436         this.store = Roo.factory(this.store, Roo.data);
437         
438         if(this.tickable){
439             this.initTickableEvents();
440             return;
441         }
442         
443         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
444         
445         if(this.hiddenName){
446             
447             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
448             
449             this.hiddenField.dom.value =
450                 this.hiddenValue !== undefined ? this.hiddenValue :
451                 this.value !== undefined ? this.value : '';
452
453             // prevent input submission
454             this.el.dom.removeAttribute('name');
455             this.hiddenField.dom.setAttribute('name', this.hiddenName);
456              
457              
458         }
459         //if(Roo.isGecko){
460         //    this.el.dom.setAttribute('autocomplete', 'off');
461         //}
462         
463         var cls = 'x-combo-list';
464         
465         //this.list = new Roo.Layer({
466         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
467         //});
468         
469         var _this = this;
470         
471         (function(){
472             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
473             _this.list.setWidth(lw);
474         }).defer(100);
475         
476         this.list.on('mouseover', this.onViewOver, this);
477         this.list.on('mousemove', this.onViewMove, this);
478         
479         this.list.on('scroll', this.onViewScroll, this);
480         
481         /*
482         this.list.swallowEvent('mousewheel');
483         this.assetHeight = 0;
484
485         if(this.title){
486             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
487             this.assetHeight += this.header.getHeight();
488         }
489
490         this.innerList = this.list.createChild({cls:cls+'-inner'});
491         this.innerList.on('mouseover', this.onViewOver, this);
492         this.innerList.on('mousemove', this.onViewMove, this);
493         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
494         
495         if(this.allowBlank && !this.pageSize && !this.disableClear){
496             this.footer = this.list.createChild({cls:cls+'-ft'});
497             this.pageTb = new Roo.Toolbar(this.footer);
498            
499         }
500         if(this.pageSize){
501             this.footer = this.list.createChild({cls:cls+'-ft'});
502             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
503                     {pageSize: this.pageSize});
504             
505         }
506         
507         if (this.pageTb && this.allowBlank && !this.disableClear) {
508             var _this = this;
509             this.pageTb.add(new Roo.Toolbar.Fill(), {
510                 cls: 'x-btn-icon x-btn-clear',
511                 text: ' ',
512                 handler: function()
513                 {
514                     _this.collapse();
515                     _this.clearValue();
516                     _this.onSelect(false, -1);
517                 }
518             });
519         }
520         if (this.footer) {
521             this.assetHeight += this.footer.getHeight();
522         }
523         */
524             
525         if(!this.tpl){
526             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
527         }
528
529         this.view = new Roo.View(this.list, this.tpl, {
530             singleSelect:true, store: this.store, selectedClass: this.selectedClass
531         });
532         //this.view.wrapEl.setDisplayed(false);
533         this.view.on('click', this.onViewClick, this);
534         
535         
536         
537         this.store.on('beforeload', this.onBeforeLoad, this);
538         this.store.on('load', this.onLoad, this);
539         this.store.on('loadexception', this.onLoadException, this);
540         /*
541         if(this.resizable){
542             this.resizer = new Roo.Resizable(this.list,  {
543                pinned:true, handles:'se'
544             });
545             this.resizer.on('resize', function(r, w, h){
546                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
547                 this.listWidth = w;
548                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
549                 this.restrictHeight();
550             }, this);
551             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
552         }
553         */
554         if(!this.editable){
555             this.editable = true;
556             this.setEditable(false);
557         }
558         
559         /*
560         
561         if (typeof(this.events.add.listeners) != 'undefined') {
562             
563             this.addicon = this.wrap.createChild(
564                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
565        
566             this.addicon.on('click', function(e) {
567                 this.fireEvent('add', this);
568             }, this);
569         }
570         if (typeof(this.events.edit.listeners) != 'undefined') {
571             
572             this.editicon = this.wrap.createChild(
573                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
574             if (this.addicon) {
575                 this.editicon.setStyle('margin-left', '40px');
576             }
577             this.editicon.on('click', function(e) {
578                 
579                 // we fire even  if inothing is selected..
580                 this.fireEvent('edit', this, this.lastData );
581                 
582             }, this);
583         }
584         */
585         
586         this.keyNav = new Roo.KeyNav(this.inputEl(), {
587             "up" : function(e){
588                 this.inKeyMode = true;
589                 this.selectPrev();
590             },
591
592             "down" : function(e){
593                 if(!this.isExpanded()){
594                     this.onTriggerClick();
595                 }else{
596                     this.inKeyMode = true;
597                     this.selectNext();
598                 }
599             },
600
601             "enter" : function(e){
602 //                this.onViewClick();
603                 //return true;
604                 this.collapse();
605                 
606                 if(this.fireEvent("specialkey", this, e)){
607                     this.onViewClick(false);
608                 }
609                 
610                 return true;
611             },
612
613             "esc" : function(e){
614                 this.collapse();
615             },
616
617             "tab" : function(e){
618                 this.collapse();
619                 
620                 if(this.fireEvent("specialkey", this, e)){
621                     this.onViewClick(false);
622                 }
623                 
624                 return true;
625             },
626
627             scope : this,
628
629             doRelay : function(foo, bar, hname){
630                 if(hname == 'down' || this.scope.isExpanded()){
631                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
632                 }
633                 return true;
634             },
635
636             forceKeyDown: true
637         });
638         
639         
640         this.queryDelay = Math.max(this.queryDelay || 10,
641                 this.mode == 'local' ? 10 : 250);
642         
643         
644         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
645         
646         if(this.typeAhead){
647             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
648         }
649         if(this.editable !== false){
650             this.inputEl().on("keyup", this.onKeyUp, this);
651         }
652         if(this.forceSelection){
653             this.inputEl().on('blur', this.doForce, this);
654         }
655         
656         if(this.multiple){
657             this.choices = this.el.select('ul.select2-choices', true).first();
658             this.searchField = this.el.select('ul li.select2-search-field', true).first();
659         }
660     },
661     
662     initTickableEvents: function()
663     {   
664         this.createList();
665         
666         if(this.hiddenName){
667             
668             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
669             
670             this.hiddenField.dom.value =
671                 this.hiddenValue !== undefined ? this.hiddenValue :
672                 this.value !== undefined ? this.value : '';
673
674             // prevent input submission
675             this.el.dom.removeAttribute('name');
676             this.hiddenField.dom.setAttribute('name', this.hiddenName);
677              
678              
679         }
680         
681 //        this.list = this.el.select('ul.dropdown-menu',true).first();
682         
683         this.choices = this.el.select('ul.select2-choices', true).first();
684         this.searchField = this.el.select('ul li.select2-search-field', true).first();
685         if(this.triggerList){
686             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
687         }
688          
689         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
690         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
691         
692         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
693         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
694         
695         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
696         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
697         
698         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
699         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
700         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
701         
702         this.okBtn.hide();
703         this.cancelBtn.hide();
704         
705         var _this = this;
706         
707         (function(){
708             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
709             _this.list.setWidth(lw);
710         }).defer(100);
711         
712         this.list.on('mouseover', this.onViewOver, this);
713         this.list.on('mousemove', this.onViewMove, this);
714         
715         this.list.on('scroll', this.onViewScroll, this);
716         
717         if(!this.tpl){
718             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
719         }
720
721         this.view = new Roo.View(this.list, this.tpl, {
722             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
723         });
724         
725         //this.view.wrapEl.setDisplayed(false);
726         this.view.on('click', this.onViewClick, this);
727         
728         
729         
730         this.store.on('beforeload', this.onBeforeLoad, this);
731         this.store.on('load', this.onLoad, this);
732         this.store.on('loadexception', this.onLoadException, this);
733         
734 //        this.keyNav = new Roo.KeyNav(this.inputEl(), {
735 //            "up" : function(e){
736 //                this.inKeyMode = true;
737 //                this.selectPrev();
738 //            },
739 //
740 //            "down" : function(e){
741 //                if(!this.isExpanded()){
742 //                    this.onTriggerClick();
743 //                }else{
744 //                    this.inKeyMode = true;
745 //                    this.selectNext();
746 //                }
747 //            },
748 //
749 //            "enter" : function(e){
750 ////                this.onViewClick();
751 //                //return true;
752 //                this.collapse();
753 //                
754 //                if(this.fireEvent("specialkey", this, e)){
755 //                    this.onViewClick(false);
756 //                }
757 //                
758 //                return true;
759 //            },
760 //
761 //            "esc" : function(e){
762 //                this.collapse();
763 //            },
764 //
765 //            "tab" : function(e){
766 //                this.collapse();
767 //                
768 //                if(this.fireEvent("specialkey", this, e)){
769 //                    this.onViewClick(false);
770 //                }
771 //                
772 //                return true;
773 //            },
774 //
775 //            scope : this,
776 //
777 //            doRelay : function(foo, bar, hname){
778 //                if(hname == 'down' || this.scope.isExpanded()){
779 //                   return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
780 //                }
781 //                return true;
782 //            },
783 //
784 //            forceKeyDown: true
785 //        });
786         
787         
788         this.queryDelay = Math.max(this.queryDelay || 10,
789                 this.mode == 'local' ? 10 : 250);
790         
791         
792         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
793         
794         if(this.typeAhead){
795             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
796         }
797     },
798
799     onDestroy : function(){
800         if(this.view){
801             this.view.setStore(null);
802             this.view.el.removeAllListeners();
803             this.view.el.remove();
804             this.view.purgeListeners();
805         }
806         if(this.list){
807             this.list.dom.innerHTML  = '';
808         }
809         
810         if(this.store){
811             this.store.un('beforeload', this.onBeforeLoad, this);
812             this.store.un('load', this.onLoad, this);
813             this.store.un('loadexception', this.onLoadException, this);
814         }
815         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
816     },
817
818     // private
819     fireKey : function(e){
820         if(e.isNavKeyPress() && !this.list.isVisible()){
821             this.fireEvent("specialkey", this, e);
822         }
823     },
824
825     // private
826     onResize: function(w, h){
827 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
828 //        
829 //        if(typeof w != 'number'){
830 //            // we do not handle it!?!?
831 //            return;
832 //        }
833 //        var tw = this.trigger.getWidth();
834 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
835 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
836 //        var x = w - tw;
837 //        this.inputEl().setWidth( this.adjustWidth('input', x));
838 //            
839 //        //this.trigger.setStyle('left', x+'px');
840 //        
841 //        if(this.list && this.listWidth === undefined){
842 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
843 //            this.list.setWidth(lw);
844 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
845 //        }
846         
847     
848         
849     },
850
851     /**
852      * Allow or prevent the user from directly editing the field text.  If false is passed,
853      * the user will only be able to select from the items defined in the dropdown list.  This method
854      * is the runtime equivalent of setting the 'editable' config option at config time.
855      * @param {Boolean} value True to allow the user to directly edit the field text
856      */
857     setEditable : function(value){
858         if(value == this.editable){
859             return;
860         }
861         this.editable = value;
862         if(!value){
863             this.inputEl().dom.setAttribute('readOnly', true);
864             this.inputEl().on('mousedown', this.onTriggerClick,  this);
865             this.inputEl().addClass('x-combo-noedit');
866         }else{
867             this.inputEl().dom.setAttribute('readOnly', false);
868             this.inputEl().un('mousedown', this.onTriggerClick,  this);
869             this.inputEl().removeClass('x-combo-noedit');
870         }
871     },
872
873     // private
874     
875     onBeforeLoad : function(combo,opts){
876         if(!this.hasFocus){
877             return;
878         }
879          if (!opts.add) {
880             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
881          }
882 //        this.restrictHeight();
883         this.selectedIndex = -1;
884     },
885
886     // private
887     onLoad : function(){
888         
889         this.hasQuery = false;
890         
891         if(!this.hasFocus){
892             return;
893         }
894         
895         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
896             this.loading.hide();
897         }
898         
899         if(this.store.getCount() > 0){
900             this.expand();
901 //            this.restrictHeight();
902             if(this.lastQuery == this.allQuery){
903                 if(this.editable && !this.tickable){
904                     this.inputEl().dom.select();
905                 }
906                 if(!this.selectByValue(this.value, true) && this.autoFocus){
907                     this.select(0, true);
908                 }
909             }else{
910                 if(this.autoFocus){
911                     this.selectNext();
912                 }
913                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
914                     this.taTask.delay(this.typeAheadDelay);
915                 }
916             }
917         }else{
918             this.onEmptyResults();
919         }
920         
921         //this.el.focus();
922     },
923     // private
924     onLoadException : function()
925     {
926         this.hasQuery = false;
927         
928         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
929             this.loading.hide();
930         }
931         
932         this.collapse();
933         Roo.log(this.store.reader.jsonData);
934         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
935             // fixme
936             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
937         }
938         
939         
940     },
941     // private
942     onTypeAhead : function(){
943         if(this.store.getCount() > 0){
944             var r = this.store.getAt(0);
945             var newValue = r.data[this.displayField];
946             var len = newValue.length;
947             var selStart = this.getRawValue().length;
948             
949             if(selStart != len){
950                 this.setRawValue(newValue);
951                 this.selectText(selStart, newValue.length);
952             }
953         }
954     },
955
956     // private
957     onSelect : function(record, index){
958         
959         if(this.fireEvent('beforeselect', this, record, index) !== false){
960         
961             this.setFromData(index > -1 ? record.data : false);
962             
963             this.collapse();
964             this.fireEvent('select', this, record, index);
965         }
966     },
967
968     /**
969      * Returns the currently selected field value or empty string if no value is set.
970      * @return {String} value The selected value
971      */
972     getValue : function(){
973         
974         if(this.multiple){
975             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
976         }
977         
978         if(this.valueField){
979             return typeof this.value != 'undefined' ? this.value : '';
980         }else{
981             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
982         }
983     },
984
985     /**
986      * Clears any text/value currently set in the field
987      */
988     clearValue : function(){
989         if(this.hiddenField){
990             this.hiddenField.dom.value = '';
991         }
992         this.value = '';
993         this.setRawValue('');
994         this.lastSelectionText = '';
995         
996     },
997
998     /**
999      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
1000      * will be displayed in the field.  If the value does not match the data value of an existing item,
1001      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
1002      * Otherwise the field will be blank (although the value will still be set).
1003      * @param {String} value The value to match
1004      */
1005     setValue : function(v){
1006         if(this.multiple){
1007             this.syncValue();
1008             return;
1009         }
1010         
1011         var text = v;
1012         if(this.valueField){
1013             var r = this.findRecord(this.valueField, v);
1014             if(r){
1015                 text = r.data[this.displayField];
1016             }else if(this.valueNotFoundText !== undefined){
1017                 text = this.valueNotFoundText;
1018             }
1019         }
1020         this.lastSelectionText = text;
1021         if(this.hiddenField){
1022             this.hiddenField.dom.value = v;
1023         }
1024         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
1025         this.value = v;
1026     },
1027     /**
1028      * @property {Object} the last set data for the element
1029      */
1030     
1031     lastData : false,
1032     /**
1033      * Sets the value of the field based on a object which is related to the record format for the store.
1034      * @param {Object} value the value to set as. or false on reset?
1035      */
1036     setFromData : function(o){
1037         
1038         if(this.multiple){
1039             if(typeof o.display_name !== 'string'){
1040                 for(var i=0;i<o.display_name.length;i++){
1041                     this.addItem({'id':o.id[i],'display_name':o.display_name[i]});
1042                 }
1043                 return;
1044             }
1045             this.addItem(o);
1046             return;
1047         }
1048             
1049         var dv = ''; // display value
1050         var vv = ''; // value value..
1051         this.lastData = o;
1052         if (this.displayField) {
1053             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1054         } else {
1055             // this is an error condition!!!
1056             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1057         }
1058         
1059         if(this.valueField){
1060             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
1061         }
1062         
1063         if(this.hiddenField){
1064             this.hiddenField.dom.value = vv;
1065             
1066             this.lastSelectionText = dv;
1067             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1068             this.value = vv;
1069             return;
1070         }
1071         // no hidden field.. - we store the value in 'value', but still display
1072         // display field!!!!
1073         this.lastSelectionText = dv;
1074         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
1075         this.value = vv;
1076         
1077         
1078     },
1079     // private
1080     reset : function(){
1081         // overridden so that last data is reset..
1082         this.setValue(this.originalValue);
1083         this.clearInvalid();
1084         this.lastData = false;
1085         if (this.view) {
1086             this.view.clearSelections();
1087         }
1088     },
1089     // private
1090     findRecord : function(prop, value){
1091         var record;
1092         if(this.store.getCount() > 0){
1093             this.store.each(function(r){
1094                 if(r.data[prop] == value){
1095                     record = r;
1096                     return false;
1097                 }
1098                 return true;
1099             });
1100         }
1101         return record;
1102     },
1103     
1104     getName: function()
1105     {
1106         // returns hidden if it's set..
1107         if (!this.rendered) {return ''};
1108         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
1109         
1110     },
1111     // private
1112     onViewMove : function(e, t){
1113         this.inKeyMode = false;
1114     },
1115
1116     // private
1117     onViewOver : function(e, t){
1118         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
1119             return;
1120         }
1121         var item = this.view.findItemFromChild(t);
1122         
1123         if(item){
1124             var index = this.view.indexOf(item);
1125             this.select(index, false);
1126         }
1127     },
1128
1129     // private
1130     onViewClick : function(view, doFocus, el, e)
1131     {
1132         var index = this.view.getSelectedIndexes()[0];
1133         
1134         var r = this.store.getAt(index);
1135         
1136         if(this.tickable){
1137             
1138             if(e.getTarget().nodeName.toLowerCase() != 'input'){
1139                 return;
1140             }
1141             
1142             var rm = false;
1143             var _this = this;
1144             
1145             Roo.each(this.tickItems, function(v,k){
1146                 
1147                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
1148                     _this.tickItems.splice(k, 1);
1149                     rm = true;
1150                     return;
1151                 }
1152             })
1153             
1154             if(rm){
1155                 return;
1156             }
1157             
1158             this.tickItems.push(r.data);
1159             return;
1160         }
1161         
1162         if(r){
1163             this.onSelect(r, index);
1164         }
1165         if(doFocus !== false && !this.blockFocus){
1166             this.inputEl().focus();
1167         }
1168     },
1169
1170     // private
1171     restrictHeight : function(){
1172         //this.innerList.dom.style.height = '';
1173         //var inner = this.innerList.dom;
1174         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
1175         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
1176         //this.list.beginUpdate();
1177         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
1178         this.list.alignTo(this.inputEl(), this.listAlign);
1179         this.list.alignTo(this.inputEl(), this.listAlign);
1180         //this.list.endUpdate();
1181     },
1182
1183     // private
1184     onEmptyResults : function(){
1185         this.collapse();
1186     },
1187
1188     /**
1189      * Returns true if the dropdown list is expanded, else false.
1190      */
1191     isExpanded : function(){
1192         return this.list.isVisible();
1193     },
1194
1195     /**
1196      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
1197      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1198      * @param {String} value The data value of the item to select
1199      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1200      * selected item if it is not currently in view (defaults to true)
1201      * @return {Boolean} True if the value matched an item in the list, else false
1202      */
1203     selectByValue : function(v, scrollIntoView){
1204         if(v !== undefined && v !== null){
1205             var r = this.findRecord(this.valueField || this.displayField, v);
1206             if(r){
1207                 this.select(this.store.indexOf(r), scrollIntoView);
1208                 return true;
1209             }
1210         }
1211         return false;
1212     },
1213
1214     /**
1215      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
1216      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
1217      * @param {Number} index The zero-based index of the list item to select
1218      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
1219      * selected item if it is not currently in view (defaults to true)
1220      */
1221     select : function(index, scrollIntoView){
1222         this.selectedIndex = index;
1223         this.view.select(index);
1224         if(scrollIntoView !== false){
1225             var el = this.view.getNode(index);
1226             if(el && !this.multiple && !this.tickable){
1227                 this.list.scrollChildIntoView(el, false);
1228             }
1229         }
1230     },
1231
1232     // private
1233     selectNext : function(){
1234         var ct = this.store.getCount();
1235         if(ct > 0){
1236             if(this.selectedIndex == -1){
1237                 this.select(0);
1238             }else if(this.selectedIndex < ct-1){
1239                 this.select(this.selectedIndex+1);
1240             }
1241         }
1242     },
1243
1244     // private
1245     selectPrev : function(){
1246         var ct = this.store.getCount();
1247         if(ct > 0){
1248             if(this.selectedIndex == -1){
1249                 this.select(0);
1250             }else if(this.selectedIndex != 0){
1251                 this.select(this.selectedIndex-1);
1252             }
1253         }
1254     },
1255
1256     // private
1257     onKeyUp : function(e){
1258         if(this.editable !== false && !e.isSpecialKey()){
1259             this.lastKey = e.getKey();
1260             this.dqTask.delay(this.queryDelay);
1261         }
1262     },
1263
1264     // private
1265     validateBlur : function(){
1266         return !this.list || !this.list.isVisible();   
1267     },
1268
1269     // private
1270     initQuery : function(){
1271         this.doQuery(this.getRawValue());
1272     },
1273
1274     // private
1275     doForce : function(){
1276         if(this.inputEl().dom.value.length > 0){
1277             this.inputEl().dom.value =
1278                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
1279              
1280         }
1281     },
1282
1283     /**
1284      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
1285      * query allowing the query action to be canceled if needed.
1286      * @param {String} query The SQL query to execute
1287      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
1288      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
1289      * saved in the current store (defaults to false)
1290      */
1291     doQuery : function(q, forceAll){
1292         
1293         if(q === undefined || q === null){
1294             q = '';
1295         }
1296         var qe = {
1297             query: q,
1298             forceAll: forceAll,
1299             combo: this,
1300             cancel:false
1301         };
1302         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
1303             return false;
1304         }
1305         q = qe.query;
1306         
1307         forceAll = qe.forceAll;
1308         if(forceAll === true || (q.length >= this.minChars)){
1309             
1310             this.hasQuery = true;
1311             
1312             if(this.lastQuery != q || this.alwaysQuery){
1313                 this.lastQuery = q;
1314                 if(this.mode == 'local'){
1315                     this.selectedIndex = -1;
1316                     if(forceAll){
1317                         this.store.clearFilter();
1318                     }else{
1319                         this.store.filter(this.displayField, q);
1320                     }
1321                     this.onLoad();
1322                 }else{
1323                     this.store.baseParams[this.queryParam] = q;
1324                     
1325                     var options = {params : this.getParams(q)};
1326                     
1327                     if(this.loadNext){
1328                         options.add = true;
1329                         options.params.start = this.page * this.pageSize;
1330                     }
1331                     
1332                     this.store.load(options);
1333                     /*
1334                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
1335                      *  we should expand the list on onLoad
1336                      *  so command out it
1337                      */
1338 //                    this.expand();
1339                 }
1340             }else{
1341                 this.selectedIndex = -1;
1342                 this.onLoad();   
1343             }
1344         }
1345         
1346         this.loadNext = false;
1347     },
1348
1349     // private
1350     getParams : function(q){
1351         var p = {};
1352         //p[this.queryParam] = q;
1353         
1354         if(this.pageSize){
1355             p.start = 0;
1356             p.limit = this.pageSize;
1357         }
1358         return p;
1359     },
1360
1361     /**
1362      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
1363      */
1364     collapse : function(){
1365         if(!this.isExpanded()){
1366             return;
1367         }
1368         
1369         this.hasFocus = false;
1370         
1371         this.list.hide();
1372         
1373         if(this.tickable){
1374             this.okBtn.hide();
1375             this.cancelBtn.hide();
1376             this.trigger.show();
1377         }
1378         
1379         Roo.get(document).un('mousedown', this.collapseIf, this);
1380         Roo.get(document).un('mousewheel', this.collapseIf, this);
1381         if (!this.editable) {
1382             Roo.get(document).un('keydown', this.listKeyPress, this);
1383         }
1384         this.fireEvent('collapse', this);
1385     },
1386
1387     // private
1388     collapseIf : function(e){
1389         var in_combo  = e.within(this.el);
1390         var in_list =  e.within(this.list);
1391         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
1392         
1393         if (in_combo || in_list || is_list) {
1394             //e.stopPropagation();
1395             return;
1396         }
1397         
1398         if(this.tickable){
1399             this.onTickableFooterButtonClick(e, false, false);
1400         }
1401
1402         this.collapse();
1403         
1404     },
1405
1406     /**
1407      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
1408      */
1409     expand : function(){
1410        
1411         if(this.isExpanded() || !this.hasFocus){
1412             return;
1413         }
1414         
1415         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
1416         this.list.setWidth(lw);
1417         
1418         
1419          Roo.log('expand');
1420         
1421         this.list.show();
1422         
1423         this.restrictHeight();
1424         
1425         if(this.tickable){
1426             
1427             this.tickItems = Roo.apply([], this.item);
1428             
1429             this.okBtn.show();
1430             this.cancelBtn.show();
1431             this.trigger.hide();
1432             
1433         }
1434         
1435         Roo.get(document).on('mousedown', this.collapseIf, this);
1436         Roo.get(document).on('mousewheel', this.collapseIf, this);
1437         if (!this.editable) {
1438             Roo.get(document).on('keydown', this.listKeyPress, this);
1439         }
1440         
1441         this.fireEvent('expand', this);
1442     },
1443
1444     // private
1445     // Implements the default empty TriggerField.onTriggerClick function
1446     onTriggerClick : function(e)
1447     {
1448         Roo.log('trigger click');
1449         
1450         if(this.disabled || !this.triggerList){
1451             return;
1452         }
1453         
1454         this.page = 0;
1455         this.loadNext = false;
1456         
1457         if(this.isExpanded()){
1458             this.collapse();
1459             if (!this.blockFocus) {
1460                 this.inputEl().focus();
1461             }
1462             
1463         }else {
1464             this.hasFocus = true;
1465             if(this.triggerAction == 'all') {
1466                 this.doQuery(this.allQuery, true);
1467             } else {
1468                 this.doQuery(this.getRawValue());
1469             }
1470             if (!this.blockFocus) {
1471                 this.inputEl().focus();
1472             }
1473         }
1474     },
1475     
1476     onTickableTriggerClick : function(e)
1477     {
1478         if(this.disabled){
1479             return;
1480         }
1481         
1482         this.page = 0;
1483         this.loadNext = false;
1484         this.hasFocus = true;
1485         
1486         if(this.triggerAction == 'all') {
1487             this.doQuery(this.allQuery, true);
1488         } else {
1489             this.doQuery(this.getRawValue());
1490         }
1491     },
1492     
1493     onSearchFieldClick : function(e)
1494     {
1495         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
1496             return;
1497         }
1498         
1499         this.page = 0;
1500         this.loadNext = false;
1501         this.hasFocus = true;
1502         
1503         if(this.triggerAction == 'all') {
1504             this.doQuery(this.allQuery, true);
1505         } else {
1506             this.doQuery(this.getRawValue());
1507         }
1508     },
1509     
1510     listKeyPress : function(e)
1511     {
1512         //Roo.log('listkeypress');
1513         // scroll to first matching element based on key pres..
1514         if (e.isSpecialKey()) {
1515             return false;
1516         }
1517         var k = String.fromCharCode(e.getKey()).toUpperCase();
1518         //Roo.log(k);
1519         var match  = false;
1520         var csel = this.view.getSelectedNodes();
1521         var cselitem = false;
1522         if (csel.length) {
1523             var ix = this.view.indexOf(csel[0]);
1524             cselitem  = this.store.getAt(ix);
1525             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
1526                 cselitem = false;
1527             }
1528             
1529         }
1530         
1531         this.store.each(function(v) { 
1532             if (cselitem) {
1533                 // start at existing selection.
1534                 if (cselitem.id == v.id) {
1535                     cselitem = false;
1536                 }
1537                 return true;
1538             }
1539                 
1540             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
1541                 match = this.store.indexOf(v);
1542                 return false;
1543             }
1544             return true;
1545         }, this);
1546         
1547         if (match === false) {
1548             return true; // no more action?
1549         }
1550         // scroll to?
1551         this.view.select(match);
1552         var sn = Roo.get(this.view.getSelectedNodes()[0])
1553         //sn.scrollIntoView(sn.dom.parentNode, false);
1554     },
1555     
1556     onViewScroll : function(e, t){
1557         
1558         if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
1559             return;
1560         }
1561         
1562         this.hasQuery = true;
1563         
1564         this.loading = this.list.select('.loading', true).first();
1565         
1566         if(this.loading === null){
1567             this.list.createChild({
1568                 tag: 'div',
1569                 cls: 'loading select2-more-results select2-active',
1570                 html: 'Loading more results...'
1571             })
1572             
1573             this.loading = this.list.select('.loading', true).first();
1574             
1575             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
1576             
1577             this.loading.hide();
1578         }
1579         
1580         this.loading.show();
1581         
1582         var _combo = this;
1583         
1584         this.page++;
1585         this.loadNext = true;
1586         
1587         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
1588         
1589         return;
1590     },
1591     
1592     addItem : function(o)
1593     {   
1594         var dv = ''; // display value
1595         
1596         if (this.displayField) {
1597             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
1598         } else {
1599             // this is an error condition!!!
1600             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
1601         }
1602         
1603         if(!dv.length){
1604             return;
1605         }
1606         
1607         var choice = this.choices.createChild({
1608             tag: 'li',
1609             cls: 'select2-search-choice',
1610             cn: [
1611                 {
1612                     tag: 'div',
1613                     html: dv
1614                 },
1615                 {
1616                     tag: 'a',
1617                     href: '#',
1618                     cls: 'select2-search-choice-close',
1619                     tabindex: '-1'
1620                 }
1621             ]
1622             
1623         }, this.searchField);
1624         
1625         var close = choice.select('a.select2-search-choice-close', true).first()
1626         
1627         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
1628         
1629         this.item.push(o);
1630         
1631         this.lastData = o;
1632         
1633         this.syncValue();
1634         
1635         this.inputEl().dom.value = '';
1636         
1637     },
1638     
1639     onRemoveItem : function(e, _self, o)
1640     {
1641         e.preventDefault();
1642         var index = this.item.indexOf(o.data) * 1;
1643         
1644         if( index < 0){
1645             Roo.log('not this item?!');
1646             return;
1647         }
1648         
1649         this.item.splice(index, 1);
1650         o.item.remove();
1651         
1652         this.syncValue();
1653         
1654         this.fireEvent('remove', this, e);
1655         
1656     },
1657     
1658     syncValue : function()
1659     {
1660         if(!this.item.length){
1661             this.clearValue();
1662             return;
1663         }
1664             
1665         var value = [];
1666         var _this = this;
1667         Roo.each(this.item, function(i){
1668             if(_this.valueField){
1669                 value.push(i[_this.valueField]);
1670                 return;
1671             }
1672
1673             value.push(i);
1674         });
1675
1676         this.value = value.join(',');
1677
1678         if(this.hiddenField){
1679             this.hiddenField.dom.value = this.value;
1680         }
1681     },
1682     
1683     clearItem : function()
1684     {
1685         if(!this.multiple){
1686             return;
1687         }
1688         
1689         this.item = [];
1690         
1691         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
1692            c.remove();
1693         });
1694         
1695         this.syncValue();
1696     },
1697     
1698     inputEl: function ()
1699     {
1700         if(this.tickable){
1701             return this.searchField;
1702         }
1703         return this.el.select('input.form-control',true).first();
1704     },
1705     
1706     
1707     onTickableFooterButtonClick : function(e, btn, el)
1708     {
1709         e.preventDefault();
1710         
1711         if(btn && btn.name == 'cancel'){
1712             this.tickItems = Roo.apply([], this.item);
1713             this.collapse();
1714             return;
1715         }
1716         
1717         this.clearItem();
1718         
1719         var _this = this;
1720         
1721         Roo.each(this.tickItems, function(o){
1722             _this.addItem(o);
1723         });
1724         
1725         this.collapse();
1726         
1727     }
1728     
1729     
1730
1731     /** 
1732     * @cfg {Boolean} grow 
1733     * @hide 
1734     */
1735     /** 
1736     * @cfg {Number} growMin 
1737     * @hide 
1738     */
1739     /** 
1740     * @cfg {Number} growMax 
1741     * @hide 
1742     */
1743     /**
1744      * @hide
1745      * @method autoSize
1746      */
1747 });