Merge branch 'hotfix/21.56.9' into master
[gitter.git] / public / js / views / controls / typeahead.js
blobd1c310de080b884c2862ae29f66a27ad8a20cab3
1 'use strict';
2 var Backbone = require('backbone');
3 var Marionette = require('backbone.marionette');
4 const debug = require('debug-proxy')('app:typeahead');
5 var Dropdown = require('./dropdown');
6 var liveSearch = require('./live-search');
8 var TypeaheadView = Marionette.ItemView.extend({
9   tagName: 'input',
10   events: {
11     keydown: 'keydown',
12     keyup: 'keyup'
13   },
14   initialize: function(options) {
15     this.disableListenToChange = options.disableListenToChange;
16     this.longDebounce = options.longDebounce;
17     this.shortDebounce = options.shortDebounce;
19     if (!this.collection) {
20       this.collection = new Backbone.Collection();
21     }
23     if (!options.disableShowOnAdd) {
24       // fix for first typeahead suggestion not triggering
25       // the dropdown to show
26       //
27       // if dropdown.show() is called on every add, then you
28       // get weird duplicates in the collectionView (!)
29       //
30       // tbh, dropdown's support for live typeaheads and static menus
31       // does not mix well.
32       this.listenToOnce(this.collection, 'add', function() {
33         this.show();
34       });
35     }
37     // May not exist
38     this.autoSelector = options.autoSelector;
40     if (options.el) {
41       this.attach();
42     }
43   },
45   onRender: function() {
46     this.attach();
47   },
49   /**
50    * clear() clears the input element's value
51    *
52    */
53   clear: function() {
54     this.el.value = '';
55   },
57   show: function() {
58     debug('show');
59     if (this.dropdown) {
60       this.dropdown.show();
61     }
62   },
64   hide: function() {
65     debug('hide');
66     if (this.dropdown) {
67       this.dropdown.hide();
68     }
69   },
71   attach: function() {
72     if (this.dropdown) return;
73     liveSearch(this, this.$el, 'searchTextChanged', {
74       immediate: 'autoSelect',
75       disableListenToChange: this.disableListenToChange,
76       longDebounce: this.longDebounce,
77       shortDebounce: this.shortDebounce
78     });
80     this.dropdown = new Dropdown({
81       collection: this.collection,
82       itemTemplate: this.options.itemTemplate,
83       itemSerializeData: this.options.itemSerializeData,
84       targetElement: this.el,
85       dropdownClass: this.options.dropdownClass,
86       backdropClass: this.options.backdropClass
87     });
89     this.listenTo(this.dropdown, 'selected', this.selected);
90   },
92   onDestroy: function() {
93     if (this.dropdown) {
94       this.dropdown.destroy();
95       this.dropdown = null;
96     }
97   },
99   selected: function(m) {
100     this.selected = m;
101     this.trigger('selected', m);
102   },
104   active: function() {
105     return this.dropdown.active();
106   },
108   autoSelect: function() {
109     var input = this.el.value;
111     if (!this.autoSelector) return;
112     if (!input) return;
114     var model = this.dropdown.getActive();
116     var predicate = this.autoSelector(input);
117     if (model && predicate(model)) return; // Existing model matches
119     var matches = this.collection.filter(predicate);
120     if (matches.length === 0) return;
122     this.dropdown.setActive(matches[0]);
123   },
124   searchTextChanged: function(input) {
125     var self = this;
127     function fetchSuccess() {
128       self.autoSelect();
129     }
130     if (this.options.fetch) {
131       this.options.fetch(input, this.collection, fetchSuccess);
132     } else {
133       this.collection.fetch(
134         { data: { q: input } },
135         { add: true, remove: true, merge: true, success: fetchSuccess }
136       );
137     }
139     this.show();
140   },
142   keydown: function(e) {
143     switch (e.keyCode) {
144       case 13:
145         this.dropdown.selectActive();
146         break;
148       case 38:
149         this.dropdown.selectPrev();
150         break;
152       case 40:
153         if (this.dropdown.active()) {
154           this.dropdown.selectNext();
155         } else {
156           this.show();
157         }
158         break;
160       case 27:
161         if (!this.dropdown.active()) {
162           // Propagate
163           return;
164         }
166         this.hide();
167         break;
169       default:
170         return;
171     }
173     e.stopPropagation();
174     e.preventDefault();
175   }
178 module.exports = TypeaheadView;