flectra.define('web.AutoComplete', function (require) { "use strict"; var Widget = require('web.Widget'); return Widget.extend({ template: "SearchView.autocomplete", // Parameters for autocomplete constructor: // // parent: this is used to detect keyboard events // // options.source: function ({term:query}, callback). This function will be called to // obtain the search results corresponding to the query string. It is assumed that // options.source will call callback with the results. // options.select: function (ev, {item: {facet:facet}}). Autocomplete widget will call // that function when a selection is made by the user // options.get_search_string: function (). This function will be called by autocomplete // to obtain the current search string. init: function (parent, options) { this._super(parent); this.$input = parent.$el; this.source = options.source; this.select = options.select; this.get_search_string = options.get_search_string; this.current_result = null; this.searching = true; this.search_string = ''; this.current_search = null; }, start: function () { var self = this; this.$input.on('keyup', function (ev) { if (ev.which === $.ui.keyCode.RIGHT) { self.searching = true; ev.preventDefault(); return; } if (ev.which === $.ui.keyCode.ENTER) { if (self.search_string.length) { self.select_item(ev); } return; } var search_string = self.get_search_string(); if (self.search_string !== search_string) { if (search_string.length) { self.search_string = search_string; self.initiate_search(search_string); } else { self.close(); } } }); this.$input.on('keypress', function (ev) { self.search_string = self.search_string + String.fromCharCode(ev.which); if (self.search_string.length) { self.searching = true; var search_string = self.search_string; self.initiate_search(search_string); } else { self.close(); } }); this.$input.on('keydown', function (ev) { switch (ev.which) { case $.ui.keyCode.ENTER: // TAB and direction keys are handled at KeyDown because KeyUp // is not guaranteed to fire. // See e.g. https://github.com/aef-/jquery.masterblaster/issues/13 case $.ui.keyCode.TAB: if (self.search_string.length) { self.select_item(ev); } break; case $.ui.keyCode.DOWN: self.move('down'); self.searching = false; ev.preventDefault(); break; case $.ui.keyCode.UP: self.move('up'); self.searching = false; ev.preventDefault(); break; case $.ui.keyCode.RIGHT: self.searching = false; var current = self.current_result; if (current && current.expand && !current.expanded) { self.expand(); self.searching = true; } ev.preventDefault(); break; case $.ui.keyCode.ESCAPE: self.close(); self.searching = false; break; } }); }, initiate_search: function (query) { if (query === this.search_string && query !== this.current_search) { this.search(query); } }, search: function (query) { var self = this; this.current_search = query; this.source({term:query}, function (results) { if (results.length) { self.render_search_results(results); self.focus_element(self.$('li:first-child')); } else { self.close(); } }); }, render_search_results: function (results) { var self = this; var $list = this.$el; $list.empty(); results.forEach(function (result) { var $item = self.make_list_item(result).appendTo($list); result.$el = $item; }); this.show(); }, make_list_item: function (result) { var self = this; var $li = $('