text: frappe.ui.form.ControlMultiSelectList = frappe.ui.form.ControlData.extend({
  	make_input() {
  		let template  = `
  			<div class="multiselect-list dropdown">
  				<div class="form-control cursor-pointer dropdown-toggle input-sm" data-toggle="dropdown" tabindex=0>
  					<span class="status-text ellipsis"></span>
  				</div>
  				<ul class="dropdown-menu">
  					<li class="dropdown-input-wrapper">
  						<input type="text" class="form-control input-sm">
  					</li>
  					<div class="selectable-items">
  					</div>
  				</ul>
  			</div>
  		`;
  
  		this.$list_wrapper = $(template);
  		this.$input = $('<input>');
  		this.$list_wrapper.prependTo(this.input_area);
  		this.$filter_input = this.$list_wrapper.find('input');
  		this.$list_wrapper.on('click', '.dropdown-menu', e => {
  			e.stopPropagation();
  		});
  		this.$list_wrapper.on('click', '.selectable-item', e => {
  			let $target = $(e.currentTarget);
  			this.toggle_select_item($target);
  		});
  		this.$list_wrapper.on('input', 'input', frappe.utils.debounce((e) => {
  			this.set_options()
  				.then(() => {
  					let txt = e.target.value;
  					let filtered_options = this._options.filter(opt => {
  						let match = false;
  						if (this.values.includes(opt.value)) {
  							return true;
  						}
  						match = Awesomplete.FILTER_CONTAINS(opt.label, txt)
  							|| Awesomplete.FILTER_CONTAINS(opt.value, txt)
  							|| Awesomplete.FILTER_CONTAINS(opt.description, txt);
  
  						return match;
  					});
  					let options = this._selected_values
  						.concat(filtered_options)
  						.uniqBy(opt => opt.value);
  					this.set_selectable_items(options);
  				});
  		}, 300));
  		this.$list_wrapper.on('keydown', 'input', e => {
  			if (e.key === 'ArrowDown') {
  				this.highlight_item(1);
  			} else if (e.key === 'ArrowUp') {
  				this.highlight_item(-1);
  			} else if (e.key === 'Enter') {
  				if (this._$last_highlighted) {
  					this.toggle_select_item(this._$last_highlighted);
  					return false;
  				}
  			}
  		});
  
  		this.$list_wrapper.on('keydown', e => {
  			if ($(e.target).is('input')) {
  				return;
  			}
  			if (e.key === 'Backspace') {
  				this.set_value([]);
  			}
  		});
  
  		this.$list_wrapper.on('show.bs.dropdown', () => {
  			this.set_options()
  				.then(() => {
  					this.set_selectable_items(this._options);
  				});
  		});
  
  		this.set_input_attributes();
  		this.values = [];
  		this._options = [];
  		this._selected_values = [];
  		this.highlighted = -1;
  	},
  
  	set_input_attributes() {
  		this.$list_wrapper
  			.attr("data-fieldtype", this.df.fieldtype)
  			.attr("data-fieldname", this.df.fieldname);
  
  		this.set_status(this.get_placeholder_text());
  
  		if (this.doctype) {
  			this.$list_wrapper.attr("data-doctype", this.doctype);
  		}
  		if(this.df.input_css) {
  			this.$list_wrapper.css(this.df.input_css);
  		}
  		if(this.df.input_class) {
  			this.$list_wrapper.addClass(this.df.input_class);
  		}
  	},
  
  	toggle_select_item($selectable_item) {
  		$selectable_item.toggleClass('selected');
  		let value = decodeURIComponent($selectable_item.data().value);
  
  		if ($selectable_item.hasClass('selected')) {
  			this.values.push(value);
  		} else {
  			this.values = this.values.filter(val => val !== value);
  		}
  		this.update_selected_values(value);
  		this.parse_validate_and_set_in_model('');
  		this.update_status();
  	},
  
  	set_value(value) {
  		if (!value) return Promise.resolve();
  		if (typeof value === 'string') {
  			value = [value];
  		}
  		this.values = value;
  		this.values.forEach(value => {
  			this.update_selected_values(value);
  		});
  		this.parse_validate_and_set_in_model('');
  		this.update_status();
  		return Promise.resolve();
  	},
  
  	update_selected_values(value) {
  		this._selected_values = this._selected_values || [];
  		let option = this._options.find(opt => opt.value === value);
  		if (option) {
  			if (this.values.includes(value)) {
  				this._selected_values.push(option);
  			} else {
  				this._selected_values = this._selected_values.filter(opt => opt.value !== value);
  			}
  		}
  	},
  
  	update_status() {
  		let text;
  		if (this.values.length === 0) {
  			text = this.get_placeholder_text();
  		} else if (this.values.length === 1) {
  			let val = this.values[0];
  			let option = this._options.find(opt => opt.value === val);
  			text = option ? option.label : val;
  		} else {
  			text = __('{0} values selected', [this.values.length]);
  		}
  		this.set_status(text);
  	},
  
  	get_placeholder_text() {
  		return `<span class="text-extra-muted">${this.df.placeholder || ''}</span>`;
  	},
  
  	set_status(text) {
  		this.$list_wrapper.find('.status-text').html(text);
  	},
  
  	set_options() {
  		let promise = Promise.resolve();
  
  		function process_options(options) {
  			return options.map(option => {
  				if (typeof option === 'string') {
  					return {
  						label: option,
  						value: option
  					};
  				}
  				if (!option.label) {
  					option.label = option.value;
  				}
  				return option;
  			});
  		}
  
  		if (this.df.get_data) {
  			let txt = this.$filter_input.val();
  			let value = this.df.get_data(txt);
  			if (!value) {
  				this._options = [];
  			} else if (value.then) {
  				promise = value.then(options => {
  					this._options = process_options(options);
  				});
  			} else {
  				this._options = process_options(value);
  			}
  		} else {
  			this._options = process_options(this.df.options);
  		}
  		return promise;
  	},
  
  	set_selectable_items(options) {
  		let html = options.map(option => {
  			let encoded_value = encodeURIComponent(option.value);
  			let selected = this.values.includes(option.value) ? 'selected' : '';
  			return `<li class="selectable-item ${selected}" data-value="${encoded_value}">
  				<div>
  					<strong>${option.label}</strong>
  					<div class="small">${option.description}</div>
  				</div>
  				<div><span class="octicon octicon-check text-muted"></span></div>
  			</li>`;
  		}).join('');
  		if (!html) {
  			html = `<li class="text-muted">${__('No values to show')}</li>`;
  		}
  		this.$list_wrapper
  			.find('.selectable-items')
  			.html(html);
  
  		this.highlighted = -1;
  	},
  
  	get_value() {
  		return this.values;
  	},
  
  	highlight_item(value) {
  		this.highlighted += value;
  
  		if (this.highlighted < 0) {
  			this.highlighted = 0;
  		}
  		let $items = this.$list_wrapper.find('.selectable-item');
  		if (this.highlighted > $items.length - 1) {
  			this.highlighted = $items.length - 1;
  		}
  
  		let $item = $items[this.highlighted];
  
  		if (this._$last_highlighted) {
  			this._$last_highlighted.removeClass('highlighted');
  		}
  		this._$last_highlighted = $($item).addClass('highlighted');
  		this.scroll_dropdown_if_needed($item);
  	},
  
  	scroll_dropdown_if_needed($item) {
  		if ($item.scrollIntoView) {
  			$item.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
  		} else {
  			$item.parentNode.scrollTop = $item.offsetTop - $item.parentNode.offsetTop;
  		}
  	}
  });
  