source: trunk/web/addons/job_monarch/lib/extjs-30/examples/image-organizer/imgorg/MultiCombo.js @ 625

Last change on this file since 625 was 625, checked in by ramonb, 15 years ago

lib/extjs-30:

  • new ExtJS 3.0
File size: 27.9 KB
Line 
1/*!
2 * Ext JS Library 3.0.0
3 * Copyright(c) 2006-2009 Ext JS, LLC
4 * licensing@extjs.com
5 * http://www.extjs.com/license
6 */
7Ext.ns('Ext.ux');
8
9/**
10 * Ext.ux.MultiCombo
11 */
12Ext.ux.MultiCombo = Ext.extend(Ext.form.ComboBox, {
13
14        /**
15         * @cfg {String} overClass [x-grid3-row-over]
16         */
17        overClass : 'x-grid3-row-over',
18        /**
19         * @cfg {Boolean} enableKeyEvents for typeAhead
20         */
21        enableKeyEvents: true,
22        /**
23         * @cfg {String} selectedClass [x-grid3-row-selected]
24         */
25        selectedClass: 'x-grid3-row-selected',
26        /**
27         * @cfg {String} highlightClass The css class applied to rows which are hovered with mouse
28         * selected via key-nav, or highlighted when a text-query matches a single item.
29         */
30        highlightClass: 'x-grid3-row-over',
31        /**
32         * @cfg {Number} autoSelectKey [44] COMMA Sets the key used to auto-select an auto-suggest
33         * highlighted query.  When pressed, the highlighted text-item will be selected as if the user
34         * selected the row with a mouse click.
35         */
36        autoSelectKey : 44,
37        /**
38         * @cfg {String} allSelectedText Text to display when all items are selected
39         */
40        allSelectedText : 'All selected',
41        /**
42         * @cfg {Number} maxDisplayRows The maximum number of rows to show before applying vscroll
43         */
44        maxDisplayRows: null,
45
46        mode: 'local',
47        triggerAction: 'all',
48        typeAhead: true,
49
50        // private
51        highlightIndex : null,
52        highlightIndexPrev : null,
53
54        query : null,
55
56
57        /**
58         * @cfg {Array} value CheckboxCombo expresses its value as an array.
59         */
60        value: [],
61
62        /**
63         * @cfg {Integer} minChars [0]
64         */
65        minChars: 0,
66
67        initComponent : function() {
68                var cls = 'x-combo-list';
69
70                // when blurring out of field, ensure that rawValue contains ONLY items contained in Store.
71                this.on('blur', this.validateSelections.createDelegate(this));
72
73                // create an auto-select key handler, like *nix-based console [tab] key behaviour
74                this.on('keypress', function(field, ev) {
75                        if (ev.getKey() == this.autoSelectKey) {        // COMMA
76                                this.onAutoSelect();
77                        }
78                },this);
79
80                this.addEvents(
81                        /**
82                         * @event initview Fires when Combo#initView is called.
83                         * gives plugins a chance to interact with DataView
84                         * @author Chris Scott
85                         * @param {Combo} this
86                         * @param {DataView} dv
87                         */
88                        'initview',
89            'clearall'
90                );
91
92                // when list expands, constrain the height with @cfg maxDisplayRows
93                if (this.maxDisplayRows) {
94                        this.on('expand', function(){
95                                var cnt = this.store.getCount();
96                                if (cnt > this.maxDisplayRows) {
97                                        var children = this.view.getNodes();
98                                        var h = 0;
99                                        for (var n = 0; n < this.maxDisplayRows; n++) {
100                                                h += Ext.fly(children[n]).getHeight();
101                                        }
102                                        this.maxHeight = h;
103                                }
104                        }, this, {
105                                single: true
106                        });
107                }
108
109                this.on('beforequery', this.onQuery, this);
110
111                // Enforce that plugins is an Array.
112                if (typeof(this.plugins) == 'undefined'){
113                        this.plugins = [];
114                }
115                else if (!Ext.isArray(this.plugins)) {
116                        this.plugins = [this.plugins];
117                }
118
119                var tmp = this.value;   // for case where transform is set.
120                Ext.ux.MultiCombo.superclass.initComponent.call(this);
121                if (this.transform) {
122                        if (typeof(tmp) == 'undefined') {
123                                tmp = [];
124                        }
125                        this.setValue(tmp);
126                }
127        },
128
129        // private
130    onViewClick : function(dv, index, node, ev){
131                var rec = this.store.getAt(index);
132                this.onSelect(rec, index);
133                this.el.focus();
134                /*
135        if(doFocus !== false){
136            this.el.focus();
137        }
138        */
139    },
140
141        // onTriggerClick, overrides Ext.form.ComboBox#onTriggerClick
142        onTriggerClick: function() {
143                if (this.highlightIndex != -1) {
144                        this.clearHighlight();
145                }
146                this.highlightIndex = -1;
147
148                if(this.disabled){
149            return;
150        }
151                if(this.isExpanded()){
152            this.collapse();
153            this.el.focus();
154        }else {
155            this.onFocus({});
156                        if(this.triggerAction == 'all') {
157                                this.doQuery(this.getRawValue(), true);
158                                var vlen = this.getValue().length, slen = this.view.getSelectedRecords().length;
159                                if (vlen != slen || vlen == 0) {
160                                        this.selectByValue(this.value, true);
161                                }
162            } else {
163                this.expand();
164                                this.doQuery(this.getRawValue());
165            }
166
167                        this.highlightIndex = -1
168                        this.highlightIndexPrev = null;
169                        this.selectNext();
170                        this.scrollIntoView();
171            this.el.focus();
172        }
173        },
174
175        // onQuery, beforequery listener, @return false
176        onQuery : function(qe) {
177                q = qe.query;
178        forceAll = qe.forceAll;
179        if(forceAll === true || (q.length >= this.minChars)){
180            if(this.lastQuery !== q){
181                                if (typeof(this.lastQuery) != 'undefined') {
182                                        if (q.match(new RegExp('^'+this.allSelectedText))) {
183                                                this.query = this.store.data;
184                                        }
185                                        else if (this.lastQuery.length > q.length) {
186                                                var items = q.replace(/\s+/g, '').split(',');
187                                                if (items[items.length-1].length == 0) {
188                                                        items.pop();
189                                                }
190                                                this.query = this.store.data.filterBy(this.store.createFilterFn(this.displayField, new RegExp('^'+items.join('$|^')+'$', "i"), false, false));
191                                        }
192                                        else {
193                                                this.query = null;
194                                        }
195                                }
196                this.lastQuery = q;
197                if(this.mode == 'local'){
198                                        var raw = this.getRawValue();
199                                        if (raw == this.allSelectedText) {
200
201                                        }
202                                        var items = raw.replace(/\s+/g, '').split(',');
203                                        var last = items.pop();
204                                        this.matches = this.store.data.filterBy(this.store.createFilterFn(this.displayField, new RegExp('^'+last, "i"), false, false)).filterBy(this.createTypeAheadFilterFn(items));
205                                        if (this.matches.getCount() == 0) {
206                                                this.clearHighlight();
207                                        }
208                                        if (q.length == 0) {
209                                                this.view.clearSelections();
210                                                this.updateValue([]);
211                                        }
212
213                    this.onLoad();
214                } else {
215                    this.store.baseParams[this.queryParam] = q;
216                    this.store.load({
217                        params: this.getParams(q)
218                    });
219                    this.expand();
220                }
221            }else{
222                this.selectedIndex = -1;
223                this.onLoad();
224            }
225        }
226
227                return false;
228        },
229
230        // onLoad, overrides Ext.form.ComboBox#onLoad
231        onLoad : function(){
232
233        if(!this.hasFocus){
234            return;
235        }
236        if(this.store.getCount() > 0){
237            if (!this.isExpanded()) {
238                                this.expand();
239                                this.restrictHeight();
240                        }
241            if(this.lastQuery == this.allQuery){
242                if(this.editable){
243                    this.el.dom.select();
244                }
245            }else{
246                                if (this.query != null) {
247                                        var values = [], indexes = [];
248                                        this.query.each(function(r){
249                                                values.push(r.data[this.valueField]);
250                                                indexes.push(this.store.indexOf(r));
251                                        }, this);
252                                        this.view.clearSelections();
253                                        this.updateValue(values, this.getRawValue());
254                                        this.view.select(indexes);
255                                }
256                                if (this.matches != null) {
257                                        if (this.matches.getCount() == 1) {
258                                                this.highlight(this.store.indexOf(this.matches.first()));
259                                                this.scrollIntoView();
260                                        }
261                                }
262                                else {
263                                        // @HACK: If store was configured with a proxy, set its mode to local now that its populated with data.
264                                        // Re-execute the query now.
265                                        this.mode = 'local';
266                                        this.lastQuery = undefined;
267                                        this.doQuery(this.getRawValue(), true);
268                                }
269                if(this.typeAhead && this.lastKey != Ext.EventObject.DOWN && this.lastKey != Ext.EventObject.BACKSPACE && this.lastKey != Ext.EventObject.DELETE){
270                                        this.taTask.delay(this.typeAheadDelay);
271                }
272            }
273        }else{
274            this.onEmptyResults();
275        }
276    },
277
278        onSelect : function(record, index) {
279                if (index == -1) {
280                        throw new Error('MultiCombo#onSelect did not receive a valid index');
281                }
282
283                // select only when user clicks [apply] button
284                if (this.selectOnApply == true) {
285                        return;
286                }
287
288                if (this.fireEvent('beforeselect', this, record, index) !== false) {
289                        var text = [];
290                        var value = [];
291                        var rs = this.view.getSelectedRecords();
292                        for (var n = 0, len = rs.length; n < len; n++) {
293                                text.push(rs[n].data[this.displayField]);
294                                value.push(rs[n].data[this.valueField]);
295                        }
296                        this.updateValue(value, (value.length != this.store.getCount()) ? text.join(', ') : this.allSelectedText);
297                        var node = this.view.getNode(index);
298                        this.innerList.scrollChildIntoView(node, false);
299                        this.fireEvent('select', this, record, index);
300                }
301        },
302
303        // private
304    onViewOver : function(ev, node){
305                var t = ev.getTarget(this.view.itemSelector);
306                if (t == null) {
307                        return;
308                }
309                this.highlightIndex = this.store.indexOf(this.view.getRecord(t));
310                this.clearHighlight();
311                this.highlight(this.highlightIndex);
312        if(this.inKeyMode){ // prevent key nav and mouse over conflicts
313            return;null
314        }
315        return;
316    },
317
318        // private
319    onTypeAhead : function(){
320                if(this.store.getCount() > 0){
321                        this.inKeyMode = false;
322            var raw = this.getRawValue();
323                        var pos = this.getCaretPosition(raw);
324                        var items = [];
325                        var query = '';
326                        if (pos !== false && pos < raw.length) {
327                                items = raw.substr(0, pos).replace(/\s+/g, '').split(',');
328                                query = items.pop();
329                        } else {
330                                items = raw.replace(/\s+/g, '').split(',');
331                                query = items.pop();
332                        }
333                        var rs = this.store.data.filterBy(this.store.createFilterFn(this.displayField, new RegExp(query, "i"), false, false)).filterBy(this.createTypeAheadFilterFn(items));
334
335                        if (rs.getCount() == 1) {
336                                var r = rs.first();
337                                var rindex = this.store.indexOf(r)
338                                if (!this.view.isSelected(rindex)) {
339                            this.typeAheadSelected = true;
340                                        var selStart = raw.length;
341                                        var len = items.join(',').length;
342                                        var selEnd = null;
343                                        var newValue = r.data[this.displayField];
344                                        if (pos !== false && pos < raw.length) {
345                                                var insertIdx = items.length;
346                                                var selStart = pos;
347                                                items = raw.replace(/\s+/g, '').split(',');
348                                                items.splice(insertIdx, 1, newValue);
349                                                selEnd = items.slice(0, insertIdx+1).join(', ').length;
350                                                this.highlight(rindex);
351                                                this.scrollIntoView();
352
353                                        }
354                                        else {
355                                                items.push(newValue);
356                                        }
357                                        var len = items.join(',').length;
358                            if(selStart != len){
359                                                var lastWord = raw.split(',').pop();
360                                                if (items.length >1 && lastWord.match(/^\s+/) == null) {
361                                                        selStart++;
362                                                }
363                                                this.setRawValue(items.join(', '));
364                                this.selectText(selStart, (selEnd!=null) ? selEnd : this.getRawValue().length);
365                            }
366                                }
367                        }
368        }
369    },
370
371        apply : function() {
372                var selected =  this.view.getSelectedRecords();
373                var value = [];
374                for (var n=0,len=selected.length;n<len;n++) {
375                        value.push(selected[n].data[this.valueField]);
376                }
377                this.setValue(value);
378        },
379
380        getCaretPosition : function(raw) {
381                raw = raw || this.getRawValue();
382                if(document.selection) {        // <-- IE, ugh:  http://parentnode.org/javascript/working-with-the-cursor-position/
383                var range = document.selection.createRange();
384                        //Save the current value. We will need this value later to find out, where the text has been changed
385                        var orig = obj.value.replace(/rn/g, "n");
386                        // replace the text
387                        range.text = text;
388                        // Now get the new content and save it into a temporary variable
389                        var actual = tmp = obj.value.replace(/rn/g, "n");
390                        /* Find the first occurance, where the original differs
391                           from the actual content. This could be the startposition
392                           of our text selection, but it has not to be. Think of the
393                           selection "ab" and replacing it with "ac". The first
394                           difference would be the "c", while the start position
395                           is the "a"
396                        */
397                        for(var diff = 0; diff < orig.length; diff++) {
398                            if(orig.charAt(diff) != actual.charAt(diff)) break;
399                        }
400
401                        /* To get the real start position, we iterate through
402                           the string searching for the whole replacement
403                           text - "abc", as long as the first difference is not
404                           reached. If you do not understand that logic - no
405                           blame to you, just copy & paste it ;)
406                        */
407                        for(var index = 0, start = 0; tmp.match(text) && (tmp = tmp.replace(text, "")) && index <= diff; index = start + text.length) {
408                            start = actual.indexOf(text, index);
409                        }
410            } else if(this.el.dom.selectionStart) {     // <-- Go the Gecko way
411                        return this.el.dom.selectionStart;
412            } else {
413                // Fallback for any other browser
414                        return false;
415            }
416        },
417
418        onAutoSelect : function() {
419                if (!this.isExpanded()) {
420                        var vlen = this.getValue().length, slen = this.view.getSelectedRecords().length;
421                        if (vlen != slen || vlen == 0) {
422                                this.selectByValue(this.value, true);
423                        }
424                }
425                var raw = this.getRawValue();
426                this.selectText(raw.length, raw.length);
427
428                var pos = this.getCaretPosition(raw);
429                var word = '';
430                if (pos !== false && pos < raw.length) {
431                        word = Ext.util.Format.trim(raw.substr(0, pos).split(',').pop());
432                } else {
433                        word = Ext.util.Format.trim(raw.split(',').pop());
434                }
435                var idx = this.store.find(this.displayField, word);
436                if (idx > -1 && !this.view.isSelected(idx)) {
437                        var rec = this.store.getAt(idx);
438                        this.select(idx);
439                }
440        },
441        // filters-out already-selected items from type-ahead queries.
442        // e.g.: if store contains: "betty, barney, bart" and betty is already selected,
443        // when user types "b", only "bart" and "barney" should be returned as possible matches,
444        // since betty is *already* selected
445        createTypeAheadFilterFn : function(items) {
446                var key = this.displayField;
447                return function(rec) {
448                        var re = new RegExp(rec.data[key], "i");
449                        var add = true;
450                        for (var n=0,len=items.length;n<len;n++) {
451                                if (re.test(items[n])) {
452                                        add = false;
453                                        break;
454                                }
455                        }
456                        return add;
457                }
458        },
459
460        updateValue : function(value, text) {
461                this.value = value;
462                if(this.hiddenField){
463                        this.hiddenField.value = value.join(',');
464                }
465                if (typeof(text) == 'string') {
466                        this.setRawValue(text);
467                }
468
469        },
470
471        /**
472         * setValue
473         * Accepts a comma-separated list of ids or an array.  if given a string, will conver to Array.
474         * @param {Array, String} v
475         */
476        setValue : function(v) {
477                var text = [];
478                var value = [];
479
480                if (typeof(v) == 'string') {    // <-- "1,2,3"
481                        value = v.match(/\d+/g); // <-- strip multiple spaces and split on ","
482            if(value){
483                            for (var n=0,len=value.length;n<len;n++) {
484                                    value[n] = parseInt(value[n]);
485                            }
486            }
487                }
488                else if (Ext.isArray(v)) {                      // <-- [1,2,3]
489                        value = v;
490                }
491                if (value && value.length) {
492                        if (this.mode == 'local') {
493                                this.updateValue(value);
494                                this.setRawValue(this.getTextValue());
495                        }
496                        else {
497                                this.updateValue(value);
498                                this.store.load({
499                                        callback: function() {
500                                                this.setRawValue(this.getTextValue());
501                                        },
502                                        scope: this
503                                });
504                                this.mode = 'local';
505                        }
506                }
507        },
508
509        getTextValue : function() {
510                if (this.value.length == this.store.getCount()) {
511                        return this.allSelectedText;
512                }
513                else {
514                        var text = [];
515                        this.store.data.filterBy(this.store.createFilterFn(this.valueField, new RegExp(this.value.join('|'), "i"), false, false)).each(function(r){
516                                text.push(r.data[this.displayField]);
517                        }, this);
518                        return text.join(', ');
519                }
520        },
521
522        /**
523     * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
524     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
525     * @param {Number} index The zero-based index of the list item to select
526     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
527     * selected item if it is not currently in view (defaults to true)
528     */
529    select : function(index, scrollIntoView){
530                if (!typeof(index) == 'number') {
531                        throw new Error('MultiCombo#select expected @param {Number} index but got: ' + typeof(index));
532                }
533        this.view.isSelected(index) ? this.view.deselect(index, true) : this.view.select(index, true);
534                this.onSelect(this.store.getAt(index), index);
535
536                this.matches = null;
537        if(scrollIntoView !== false){
538            var el = this.view.getNode(index);
539            if(el){
540                this.innerList.scrollChildIntoView(el, false);
541            }
542        }
543
544    },
545
546        getLastValue : function() {
547                return Ext.util.Format.trim(this.getRawValue().split(',').pop());
548        },
549
550        /**
551     * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
552     * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
553     * @param {String} value The data value of the item to select
554     * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
555     * selected item if it is not currently in view (defaults to true)
556     * @return {Boolean} True if the value matched an item in the list, else false
557     */
558    selectByValue : function(v, scrollIntoView){
559                if (v.length) {
560                        var indexes = [];
561                        var rs = this.store.data.filterBy(this.store.createFilterFn(this.valueField, new RegExp(v.join('|'), "i"))).each(function(r){
562                                indexes.push(this.store.indexOf(r));
563                        }, this);
564                        if (indexes.length) {
565                                this.view.select(indexes);
566                                return true;
567                        }
568                }
569                else {
570                        this.view.clearSelections();
571                        this.setRawValue('');
572                        return false;
573                }
574    },
575
576        // private
577    initEvents : function(){
578        Ext.form.ComboBox.superclass.initEvents.call(this);
579        this.keyNav = new Ext.KeyNav(this.el, {
580            "up" : function(e){
581                                this.lastKey = Ext.EventObject.UP;
582                this.inKeyMode = true;
583                this.selectPrev();
584                                this.scrollIntoView();
585            },
586
587            "down" : function(e){
588                this.inKeyMode = true;
589                                if(!this.isExpanded()){
590                                        this.lastKey = Ext.EventObject.DOWN;
591                    this.onTriggerClick();
592                }else{
593                    this.selectNext();
594                                        this.scrollIntoView();
595                }
596
597            },
598
599            "enter" : function(e){
600                                var idx = this.highlightIndex;
601                                if (this.inKeyMode === true) {
602                                        if (this.plugins.length && (idx <= -1)) {
603                                                if (this.plugins[idx + 1]) {
604                                                        this.plugins[idx + 1].onEnter(this);
605                                                }
606                                        }
607                                        else
608                                                if (this.plugins.length && this.highlightIndex == 0 && this.highlightIndexPrev == -1) {
609                                                        if (this.plugins[idx]) {
610                                                                this.plugins[idx].onEnter(this);
611                                                        }
612                                                }
613                                                else {
614                                                        var idx = this.getHighlightedIndex() || 0;
615                                                        if (this.highlightIndex != null && idx != null) {
616                                                                this.select(idx, true);
617                                                                //this.delayedCheck = true;
618                                                                //this.unsetDelayCheck.defer(10, this);
619
620                                                        }
621                                                }
622                                }
623                                else {
624                                        var v = this.getLastValue();
625                                        var raw = this.getRawValue();
626
627                                        /** this block should be moved to method getCurrentWord
628                                         *
629                                         */
630                                        var pos = this.getCaretPosition(raw);
631                                        var word = '';
632                                        if (pos !== false && pos < raw.length) {
633                                                word = Ext.util.Format.trim(raw.substr(0, pos).split(',').pop());
634                                        } else {
635                                                word = Ext.util.Format.trim(raw.split(',').pop());
636                                        }
637                                        /*******************************************************/
638
639                                        var idx = this.store.find(this.displayField, word);
640                                        if (idx != -1) {
641                                                var rec = this.store.getAt(idx);
642                                                this.select(idx, true);
643                                        }
644                                        raw = this.getRawValue();
645                                        this.selectText(raw.length, raw.length);
646                                        this.collapse();
647                                }
648            },
649
650            "esc" : function(e){
651                this.collapse();
652            },
653
654            "tab" : function(e){
655                                if (this.matches != null && this.matches.getCount() == 1) {
656                                        var idx = this.store.indexOf(this.matches.first());
657                                        if (!this.view.isSelected(idx)) {
658                                                this.select(this.store.indexOf(this.matches.first()), true);
659                                        }
660                                }
661                                else if (this.value.length == 0 && this.getRawValue().length > 0) {
662                                        this.setRawValue('');
663                                }
664                                this.collapse();
665                return true;
666            },
667
668            scope : this,
669
670            doRelay : function(foo, bar, hname){
671                if(hname == 'down' || this.scope.isExpanded()){
672                   return Ext.KeyNav.prototype.doRelay.apply(this, arguments);
673                }
674                return true;
675            },
676
677            forceKeyDown : true
678        });
679        this.queryDelay = Math.max(this.queryDelay || 10,
680                this.mode == 'local' ? 10 : 250);
681        this.dqTask = new Ext.util.DelayedTask(this.initQuery, this);
682        if(this.typeAhead){
683            this.taTask = new Ext.util.DelayedTask(this.onTypeAhead, this);
684        }
685        if(this.editable !== false){
686            this.el.on("keyup", this.onKeyUp, this);
687        }
688        if(this.forceSelection){
689            this.on('blur', this.doForce, this);
690        }
691    },
692
693        // private, blur-handler to ensure that rawValue contains only values from selections, in the same order as selected
694        validateSelections : function(field) {
695                var v = this.getValue();
696                var text = [];
697                for (var i=0,len=v.length;i<len;i++) {
698                        var idx = this.store.find(this.valueField, v[i]);
699                        if (idx >=0) {
700                                text.push(this.store.getAt(idx).data[this.displayField]);
701                        }
702                }
703                this.setRawValue(text.join(', '));
704        },
705
706        scrollIntoView : function() {
707                var el = this.getHighlightedNode();
708                if (el) {
709                        this.innerList.scrollChildIntoView(el);
710                }
711        },
712
713        // private
714    selectNext : function(){
715                this.clearHighlight();
716                if (this.highlightIndex == null) {
717                        this.highlightIndex = -1;
718                }
719                if (this.highlightIndex <= -1 && this.highlightIndexPrev != -1) {
720                        if (this.plugins.length > 0) {
721                                var idx = Math.abs(this.highlightIndex)-1;
722                                if (this.plugins.length >= Math.abs(this.highlightIndex)) {
723                                        this.plugins[idx].selectNext(this);
724                                        this.highlightIndexPrev = this.highlightIndex;
725                                        this.highlightIndex++;
726                                        return false;
727                                }
728                        }
729                }
730                if (this.highlightIndexPrev == -1 && this.highlightIndex == 0) {
731                        this.highlightIndex = -1;
732                }
733                var ct = this.store.getCount();
734                if(ct > 0){
735            if (this.highlightIndex == -1 || this.highlightIndex+1 < ct) {
736                                if (this.highlightIndex == -1) {
737                                        this.highlightIndexPrev = 0;
738                                }
739                                else {
740                                        this.highlightIndexPrev = this.highlightIndex -1;
741                                }
742                                this.highlight(++this.highlightIndex);
743
744                        }
745                        else {
746                                this.highlight(ct-1);
747                        }
748        }
749    },
750
751    // private
752    selectPrev : function(){
753                this.clearHighlight();
754                if (this.highlightIndex <= 0) {
755                        var idx = Math.abs(this.highlightIndex);
756                        if (this.plugins.length >= idx+1 && this.highlightIndexPrev >= 0) {
757                                this.clearHighlight();
758                                this.plugins[idx].selectPrev(this);
759                                this.highlightIndexPrev = this.highlightIndex;
760                                this.highlightIndex--;
761                                if (this.highlightIndex == -1) {
762                                        this.highlightIndexPrev = -1;
763                                }
764                                return false;
765                        }
766                        else {
767                                this.highlightIndex = -1;
768                                this.highlightIndexPrev = -1;
769                                this.collapse();
770                                return;
771                        }
772                }
773
774                this.highlightIndexPrev = this.highlightIndex;
775        var ct = this.store.getCount();
776        if(ct > 0){
777                        if (this.highlighIndex == -1) {
778                                this.highlightIndex = 0;
779                        }
780                        else if (this.highlightIndex != 0) {
781                                this.highlightIndex--;
782                        }
783                        else if (this.highlightIndex == 0) {
784                                this.collapse();
785                        }
786                        this.highlight(this.highlightIndex);
787        }
788    },
789
790        collapse : function() {
791                if (this.isExpanded()) {
792                        this.highlightIndex = null;
793                        this.highlightIndexPrev = null;
794                }
795                Ext.ux.MultiCombo.superclass.collapse.call(this);
796        },
797
798        highlight : function(index) {
799                this.view.el.select('.'+this.highlightClass).removeClass(this.highlightClass);
800                var node = Ext.fly(this.view.getNode(index));
801                if (node) {
802                        node.addClass(this.highlightClass);
803                }
804        },
805
806        getHighlightedIndex : function() {
807                var node = this.view.el.child('.' + this.highlightClass, true);
808                return (node) ? this.store.indexOf(this.view.getRecord(node)) : this.highlightIndex;
809        },
810        getHighlightedNode : function() {
811                return this.view.el.child('.'+this.highlightClass, true);
812        },
813
814        clearHighlight : function() {
815                if (typeof(this.view) != 'object') { return false; }
816                var el = this.view.el.select('.'+this.highlightClass);
817                if (el) {
818                        el.removeClass(this.highlightClass);
819                }
820        },
821
822    // private
823    initList : function(){
824        if(!this.list){
825            var cls = 'x-combo-list';
826
827            this.list = new Ext.Layer({
828                shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
829            });
830
831            var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
832            this.list.setWidth(lw);
833            this.list.swallowEvent('mousewheel');
834            this.assetHeight = 0;
835            if(this.syncFont !== false){
836                this.list.setStyle('font-size', this.el.getStyle('font-size'));
837            }
838            if(this.title){
839                this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
840                this.assetHeight += this.header.getHeight();
841            }
842
843            this.innerList = this.list.createChild({cls:cls+'-inner'});
844            this.innerList.on('mouseover', this.onViewOver, this);
845            this.innerList.on('mousemove', this.onViewMove, this);
846            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
847
848            if(this.pageSize){
849                this.footer = this.list.createChild({cls:cls+'-ft'});
850                this.pageTb = new Ext.PagingToolbar({
851                    store:this.store,
852                    pageSize: this.pageSize,
853                    renderTo:this.footer
854                });
855                this.assetHeight += this.footer.getHeight();
856            }
857
858            if(!this.tpl){
859                /**
860                * @cfg {String/Ext.XTemplate} tpl The template string, or {@link Ext.XTemplate}
861                * instance to use to display each item in the dropdown list. Use
862                * this to create custom UI layouts for items in the list.
863                * <p>
864                * If you wish to preserve the default visual look of list items, add the CSS
865                * class name <pre>x-combo-list-item</pre> to the template's container element.
866                * <p>
867                * <b>The template must contain one or more substitution parameters using field
868                * names from the Combo's</b> {@link #store Store}. An example of a custom template
869                * would be adding an <pre>ext:qtip</pre> attribute which might display other fields
870                * from the Store.
871                * <p>
872                * The dropdown list is displayed in a DataView. See {@link Ext.DataView} for details.
873                */
874                this.tpl = '<tpl for="."><div class="'+cls+'-item">{' + this.displayField + '}</div></tpl>';
875                /**
876                 * @cfg {String} itemSelector
877                 * <b>This setting is required if a custom XTemplate has been specified in {@link #tpl}
878                 * which assigns a class other than <pre>'x-combo-list-item'</pre> to dropdown list items</b>.
879                 * A simple CSS selector (e.g. div.some-class or span:first-child) that will be
880                 * used to determine what nodes the DataView which handles the dropdown display will
881                 * be working with.
882                 */
883            }
884
885            /**
886            * The {@link Ext.DataView DataView} used to display the ComboBox's options.
887            * @type Ext.DataView
888            */
889            this.view = new Ext.DataView({
890                applyTo: this.innerList,
891                tpl: this.tpl,
892                                simpleSelect: true,
893                multiSelect: true,
894                                overClass: this.overClass,
895                selectedClass: this.selectedClass,
896                itemSelector: this.itemSelector || '.' + cls + '-item'
897            });
898            this.view.on('click', this.onViewClick, this);
899                        this.fireEvent('initview', this, this.view);
900            this.bindStore(this.store, true);
901
902            if(this.resizable){
903                this.resizer = new Ext.Resizable(this.list,  {
904                   pinned:true, handles:'se'
905                });
906                this.resizer.on('resize', function(r, w, h){
907                    this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
908                    this.listWidth = w;
909                    this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
910                    this.restrictHeight();
911                }, this);
912                this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
913            }
914        }
915    }
916});
917
918
919Ext.reg('multicombo', Ext.ux.MultiCombo);
Note: See TracBrowser for help on using the repository browser.