text: /* tslint:disable:no-null-keyword */
  /* tslint:disable:max-line-length */
  /* tslint:disable:no-bitwise */
  import { Component, Renderer2, forwardRef, ElementRef, Input, Output, EventEmitter, ContentChild, ViewChild, ViewContainerRef, HostBinding, isDevMode, Optional, Inject, ChangeDetectorRef, NgZone } from '@angular/core';
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
  import { SearchBarComponent } from './searchbar.component';
  import { ItemTemplateDirective } from './templates/item-template.directive';
  import { HeaderTemplateDirective } from './templates/header-template.directive';
  import { FooterTemplateDirective } from './templates/footer-template.directive';
  import { NoDataTemplateDirective } from './templates/no-data-template.directive';
  import { SelectionService } from './selection.service';
  import { NavigationService } from './navigation.service';
  import { DisabledItemsService } from './disabled-items.service';
  import { merge } from 'rxjs/observable/merge';
  import { catchError } from 'rxjs/operators/catchError';
  import { distinctUntilChanged } from 'rxjs/operators/distinctUntilChanged';
  import { filter } from 'rxjs/operators/filter';
  import { map } from 'rxjs/operators/map';
  import { partition } from 'rxjs/operators/partition';
  import { tap } from 'rxjs/operators/tap';
  import { throttleTime } from 'rxjs/operators/throttleTime';
  import { Subject } from 'rxjs/Subject';
  import { Subscription } from 'rxjs/Subscription';
  import { isPresent, isChanged, guid, isDocumentAvailable, getter, resolveValue } from './util';
  import { NavigationAction } from './navigation-action';
  import { Keys } from './common/keys';
  import { PreventableEvent } from './common/preventable-event';
  import { LocalizationService, L10N_PREFIX } from '@progress/kendo-angular-l10n';
  import { PopupService } from '@progress/kendo-angular-popup';
  import { TOUCH_ENABLED } from './touch-enabled';
  import { ComboBoxMessages } from './error-messages';
  import { FilterableDropDownComponentBase } from './filterable-base.component';
  var InternalState;
  (function (InternalState) {
      InternalState[InternalState["None"] = 0] = "None";
      InternalState[InternalState["SetInitial"] = 1] = "SetInitial";
      InternalState[InternalState["UseModel"] = 2] = "UseModel";
      InternalState[InternalState["UseExisting"] = 4] = "UseExisting";
      InternalState[InternalState["UseCustom"] = 8] = "UseCustom";
      InternalState[InternalState["UseFilter"] = 16] = "UseFilter";
      InternalState[InternalState["UseEmpty"] = 32] = "UseEmpty";
  })(InternalState || (InternalState = {}));
  /**
   * @hidden
   */
  export const COMBOBOX_VALUE_ACCESSOR = {
      multi: true,
      provide: NG_VALUE_ACCESSOR,
      // tslint:disable-next-line:no-use-before-declare
      useExisting: forwardRef(() => ComboBoxComponent)
  };
  /**
   * Represents the Kendo UI ComboBox component for Angular.
   *
   * @example
   * ```ts
   * _@Component({
   * selector: 'my-app',
   * template: `
   *  <kendo-combobox [data]="listItems">
   *  </kendo-combobox>
   * `
   * })
   * class AppComponent {
   *   public listItems: Array<string> = ["Item 1", "Item 2", "Item 3", "Item 4"];
   * }
   * ```
   */
  export class ComboBoxComponent {
      constructor(localization, popupService, selectionService, navigationService, _zone, cdr, renderer, wrapper, touchEnabled) {
          this.localization = localization;
          this.popupService = popupService;
          this.selectionService = selectionService;
          this.navigationService = navigationService;
          this._zone = _zone;
          this.cdr = cdr;
          this.renderer = renderer;
          this.touchEnabled = touchEnabled;
          this.selected = [];
          /**
           * @hidden
           */
          this.focusableId = `k-${guid()}`;
          /**
           * Specifies whether the ComboBox allows user-defined values that are not present in the dataset.
           * The default value is `false`.
           * For more information, refer to the article on [custom values]({% slug custom_values_combobox %}).
           */
          this.allowCustom = false;
          /**
           * A user-defined callback which returns normalized custom values. Typically used when the data items are different from type `string`.
           * @param { Any } value - The custom value defined by the user.
           * @returns { Any }
           *
           * @example
           * ```ts
           * import { map } from 'rxjs/operators/map';
           *
           * _@Component({
           * selector: 'my-app',
           * template: `
           *   <kendo-combobox
           *       [allowCustom]="true"
           *       [data]="listItems"
           *       [textField]="'text'"
           *       [valueField]="'value'"
           *       [valueNormalizer]="valueNormalizer"
           *       (valueChange)="onValueChange($event)"
           *   >
           *   </kendo-combobox>
           * `
           * })
           *
           * class AppComponent {
           *   public listItems: Array<{ text: string, value: number }> = [
           *       { text: "Small", value: 1 },
           *       { text: "Medium", value: 2 },
           *       { text: "Large", value: 3 }
           *   ];
           *
           *   public onValueChange(value) {
           *       console.log("valueChange : ", value);
           *   }
           *
           *   public valueNormalizer = (text$: Observable<string>) => text$.pipe(map((text: string) => {
           *      return { ProductID: null, ProductName: text };
           *   }));
           *
           * }
           * ```
           */
          this.valueNormalizer = (text) => text.pipe(map((userInput) => userInput));
          /**
           * The hint displayed when the component is empty.
           *
           */
          this.placeholder = "";
          /**
           * Sets the height of the suggestions list. By default, `listHeight` is 200px.
           *
           * > The `listHeight` property affects only the list of suggestions and not the whole popup container.
           * > To set the height of the popup container, use `popupSettings.height`.
           */
          this.listHeight = 200;
          /**
           * @hidden
           *
           * Enables the auto-completion of the text based on the first data item.
           */
          this.suggest = false;
          /**
           * If set to `true`, renders a button on hovering over the component. Clicking this button resets the value of the component to `undefined` and triggers the `change` event.
           */
          this.clearButton = true;
          /**
           * Sets the disabled state of the component.
           */
          this.disabled = false;
          /**
           * Sets the read-only state of the component.
           */
          this.readonly = false;
          /**
           * Specifies the [`tabindex`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex) of the component.
           */
          this.tabindex = 0;
          /**
           * Enables the filtering functionality. If set to `true`, the component emits the `filterChange` event.
           */
          this.filterable = false;
          /**
           * Fires each time the value is changed&mdash;
           * when the component is blurred or the value is cleared through the **Clear** button.
           * For more details, refer to the example on [events]({% slug overview_combobox %}#toc-events).
           * When the value of the component is programmatically changed to `ngModel` or `formControl`
           * through its API or form binding, the `valueChange` event is not triggered because it
           * might cause a mix-up with the built-in `valueChange` mechanisms of the `ngModel` or `formControl` bindings.
           */
          this.valueChange = new EventEmitter();
          /**
           * Fires each time an item selection is changed.
           * For more details, refer to the example on [events]({% slug overview_combobox %}#toc-events).
           */
          this.selectionChange = new EventEmitter();
          /**
           * Fires each time the user types in the input field.
           * You can filter the source based on the passed filtration value.
           * For more details, refer to the example on [events]({% slug overview_combobox %}#toc-events).
           */
          this.filterChange = new EventEmitter();
          /**
           * Fires each time the popup is about to open.
           * This event is preventable. If you cancel it, the popup will remain closed.
           */
          this.open = new EventEmitter();
          /**
           * Fires each time the popup is about to close.
           * This event is preventable. If you cancel it, the popup will remain open.
           */
          this.close = new EventEmitter();
          /**
           * Fires each time the user focuses the ComboBox.
           */
          this.onFocus = new EventEmitter(); //tslint:disable-line:no-output-rename
          /**
           * Fires each time the ComboBox gets blurred.
           */
          this.onBlur = new EventEmitter(); //tslint:disable-line:no-output-rename
          this.isFocused = false;
          this.listBoxId = guid();
          this.optionPrefix = guid();
          this.onChangeCallback = (_) => { };
          this.onTouchedCallback = (_) => { };
          this.observableSubscriptions = new Subscription();
          this._state = InternalState.SetInitial;
          this._filtering = false;
          this._text = "";
          this._open = false;
          this._popupSettings = { animate: true };
          this.popupMouseDownHandler = (event) => event.preventDefault();
          this.customValueSubject = new Subject();
          this.valueSubject = new Subject();
          this.selectionSubject = new Subject();
          this.direction = localization.rtl ? 'rtl' : 'ltr';
          this.wrapper = wrapper.nativeElement;
          this.data = [];
          this.subscribeEvents();
      }
      get width() {
          let wrapperOffsetWidth = 0;
          if (isDocumentAvailable()) {
              wrapperOffsetWidth = this.wrapper.offsetWidth;
          }
          const width = this.popupSettings.width || wrapperOffsetWidth;
          const minWidth = isNaN(wrapperOffsetWidth) ? wrapperOffsetWidth : `${wrapperOffsetWidth}px`;
          const maxWidth = isNaN(width) ? width : `${width}px`;
          return { min: minWidth, max: maxWidth };
      }
      get height() {
          const popupHeight = this.popupSettings.height;
          return isPresent(popupHeight) ? `${popupHeight}px` : 'auto';
      }
      set text(text) {
          let textCandidate = getter(text, this.textField, true);
          this._text = isPresent(textCandidate) ? textCandidate.toString() : "";
      }
      get text() {
          return this._text;
      }
      set popupOpen(open) {
          if (this.disabled || this.readonly || this.popupOpen === open) {
              return;
          }
          const eventArgs = new PreventableEvent();
          if (open) {
              this.open.emit(eventArgs);
          }
          else {
              this.close.emit(eventArgs);
          }
          if (eventArgs.isDefaultPrevented()) {
              return;
          }
          this._toggle(open);
      }
      get popupOpen() {
          return this._open;
      }
      get activeDescendant() {
          const dataItem = this.data[this.selectionService.focused];
          return this.optionPrefix + "-" + dataItem ? getter(dataItem, this.valueField) : "";
      }
      get noDataLabel() {
          if (this.data.length === 0) {
              return this.noDataText;
          }
      }
      get appendTo() {
          const { appendTo } = this.popupSettings;
          if (!appendTo || appendTo === 'root') {
              return undefined;
          }
          return appendTo === 'component' ? this.container : appendTo;
      }
      /**
       * Sets the data of the ComboBox.
       *
       * > The data has to be provided in an array-like list.
       */
      set data(data) {
          this._data = data || [];
          this.setState();
          if (this._filtering) {
              if (this.text.length > 0) {
                  this.search(this.text);
                  if (this.selectionService.focused === -1 && data.length) {
                      this.selectionService.focused = 0;
                  }
              }
              else {
                  this.selectionService.focused = -1;
              }
          }
          if (this.suggest && this.data && this.data.length && this.text) {
              this.suggestedText = getter(this.data[0], this.textField);
          }
      }
      get data() {
          return this._data;
      }
      /**
       * Sets the value of the ComboBox. It can either be of the primitive (string, numbers) or of the complex (objects) type. To define the type, use the `valuePrimitive` option.
       *
       * > All selected values which are not present in the dataset are considered custom values. When the `Enter` key is pressed or the component loses focus, custom values get dismissed unless `allowCustom` is set to `true`.
       */
      set value(newValue) {
          this.verifySettings(newValue);
          this._value = newValue;
          this.cdr.markForCheck();
      }
      get value() {
          return this._value;
      }
      /**
       * Configures the popup of the ComboBox.
       *
       * The available options are:
       * - `animate: Boolean`&mdash;Controls the popup animation. By default, the open and close animations are enabled.
       * - `width: Number | String`&mdash;Sets the width of the popup container. By default, the width of the host element is used  If set to `auto`, the component automatically adjusts the width of the popup, so no item labels are wrapped.
       * - `height: Number`&mdash;Sets the height of the popup container.
       * - `popupClass: String`&mdash;Specifies a list of CSS classes that are used to style the popup.
       * - `appendTo: "root" | "component" | ViewContainerRef`&mdash;Specifies the component to which the popup will be appended.
       */
      set popupSettings(settings) {
          this._popupSettings = Object.assign({ animate: true }, settings);
      }
      get popupSettings() {
          return this._popupSettings;
      }
      /**
       * @hidden
       */
      set tabIndex(tabIndex) {
          this.tabindex = tabIndex;
      }
      get tabIndex() {
          return this.tabindex;
      }
      get widgetClasses() {
          return true;
      }
      /**
       * @hidden
       */
      wrapperClasses() {
          return {
              'k-dropdown-wrap': true,
              'k-state-default': !this.disabled,
              'k-state-disabled': this.disabled,
              'k-state-focused': this.isFocused
          };
      }
      get clearable() {
          return this.clearButton;
      }
      get dir() {
          return this.direction;
      }
      ngOnInit() {
          this.renderer.removeAttribute(this.wrapper, "tabindex");
          this.createSelectionStream();
          this.createValueStream();
          this.localizationChangeSubscription = this.localization
              .changes.subscribe(({ rtl }) => {
              this.direction = rtl ? 'rtl' : 'ltr';
              this.setMessages();
          });
          this.setMessages();
      }
      createSelectionStream() {
          if (this.selectionSubscription) {
              this.selectionSubscription.unsubscribe();
          }
          this.selectionSubscription =
              this.selectionSubject.pipe(map((index) => {
                  return {
                      dataItem: this.data[index],
                      value: getter(this.data[index], this.valueField)
                  };
              }), distinctUntilChanged((prev, next) => {
                  return prev.value === next.value;
              }))
                  .subscribe((args) => {
                  this.selectionChange.emit(args.dataItem);
              });
      }
      createValueStream() {
          const valueStream = this.valueSubject.pipe(filter((candidate) => {
              const current = this.valuePrimitive ? this.value : getter(this.value, this.valueField);
              const newValue = getter(candidate, this.valueField);
              let newText = getter(candidate, this.textField);
              if (isPresent(newText)) {
                  newText = newText.toString();
              }
              if (current === newValue && this.text === newText) {
                  return false;
              }
              else {
                  return true;
              }
          }), map((candidate) => {
              const newValue = getter(candidate, this.valueField);
              const newText = getter(candidate, this.textField);
              return {
                  dataItem: candidate,
                  text: newText,
                  value: this.valuePrimitive ? newValue : candidate
              };
          }));
          const customValueStreams = partition(() => this.allowCustom)(this.customValueSubject.pipe(throttleTime(300)));
          const allowCustomValueStream = customValueStreams[0].pipe(tap(() => {
              this.loading = true;
              this.disabled = true;
          }), filter(() => {
              const hasChange = this.text !== getter(this.value, this.textField);
              this.loading = hasChange;
              this.disabled = hasChange;
              return hasChange;
          }), this.valueNormalizer, map((normalizedValue) => {
              return {
                  dataItem: normalizedValue,
                  text: this.text,
                  value: normalizedValue
              };
          }));
          const disableCustomValueStream = customValueStreams[1].pipe(map(() => {
              return {
                  dataItem: undefined,
                  text: undefined,
                  value: undefined
              };
          }));
          if (this.valueSubscription) {
              this.valueSubscription.unsubscribe();
          }
          const merged = merge(valueStream, allowCustomValueStream, disableCustomValueStream);
          this.valueSubscription = merged.pipe(catchError(() => {
              this.dataItem = undefined;
              this.value = undefined;
              this.text = undefined;
              this.loading = false;
              this.disabled = false;
              this.emitChange();
              return merged;
          }))
              .subscribe((state) => {
              this.dataItem = state.dataItem;
              this.value = state.value;
              this.text = state.text;
              this.loading = false;
              this.disabled = false;
              if (this.filterable && !isPresent(state.value) && !isPresent(state.text) && !isPresent(this._previousValue)) {
                  this.filterChange.emit("");
              }
              this.emitChange();
          });
      }
      subscribeEvents() {
          if (!isDocumentAvailable()) {
              return;
          }
          [
              this.selectionService.onChange.pipe(filter((event) => isPresent(event.indices[0])), map((event) => event.indices[0]))
                  .subscribe(this.handleItemChange.bind(this)),
              this.selectionService.onSelect.pipe(filter((event) => isPresent(event.indices[0])), map((event) => event.indices[0]))
                  .subscribe(this.handleItemSelect.bind(this)),
              merge(this.navigationService.up, this.navigationService.down, this.navigationService.home, this.navigationService.end).subscribe((event) => this.navigate(event.index)),
              this.navigationService.open.subscribe(() => this.popupOpen = true),
              this.navigationService.close.subscribe(() => this.popupOpen = false),
              this.navigationService.enter.subscribe((event) => {
                  if (this.popupOpen) {
                      event.originalEvent.preventDefault();
                  }
                  this.confirmSelection();
              }),
              this.navigationService.esc.subscribe(this.handleBlur.bind(this))
          ].forEach(s => this.observableSubscriptions.add(s));
      }
      unsubscribeEvents() {
          if (!isDocumentAvailable()) {
              return;
          }
          this.observableSubscriptions.unsubscribe();
          if (this.valueSubscription) {
              this.valueSubscription.unsubscribe();
          }
          if (this.selectionSubscription) {
              this.selectionSubscription.unsubscribe();
          }
      }
      handleItemChange(index) {
          this._filtering = false;
          this.change(this.data[index]);
      }
      handleItemSelect(index) {
          this._filtering = false;
          if (!this.popupOpen) {
              this.change(this.data[index]);
          }
          else {
              this.selectionSubject.next(index);
          }
      }
      ngOnDestroy() {
          this._toggle(false);
          this.unsubscribeEvents();
          clearTimeout(this.messagesTimeout);
          if (this.localizationChangeSubscription) {
              this.localizationChangeSubscription.unsubscribe();
          }
      }
      ngOnChanges(changes) {
          if (changes.hasOwnProperty("value")) {
              this._state |= InternalState.UseModel;
              this._modelValue = changes.value.currentValue;
          }
          if (isChanged("valueNormalizer", changes)) {
              this.createSelectionStream();
              this.createValueStream();
          }
          if (this.valuePrimitive === undefined) {
              this.valuePrimitive = this.valueField ? false : true;
          }
          const STATE_PROPS = /(value|textField|valueField|valuePrimitive)/g;
          if (STATE_PROPS.test(Object.keys(changes).join())) {
              this.setState();
          }
      }
      /**
       * Focuses the ComboBox.
       */
      focus() {
          if (!this.disabled) {
              this.searchbar.focus();
          }
      }
      /**
       * Blurs the ComboBox.
       */
      blur() {
          if (!this.disabled) {
              this.searchbar.blur();
          }
      }
      /**
       * Toggles the visibility of the popup. If you use the `toggle` method to open or close the popup,
       * the `open` and `close` events will not be fired.
       *
       * @param open - The state of the popup.
       */
      toggle(open) {
          Promise.resolve(null).then(() => {
              this._toggle((open === undefined) ? !this._open : open);
              this.cdr.markForCheck();
          });
      }
      /**
       * Returns the current open state of the popup.
       */
      get isOpen() {
          return this.popupOpen;
      }
      /**
       * Resets the value of the ComboBox.
       * If you use the `reset` method to clear the value of the component,
       * the model will not update automatically and the `selectionChange` and `valueChange` events will not be fired.
       */
      reset() {
          this._modelValue = undefined;
          this.setState();
      }
      /**
       * @hidden
       */
      clearValue(event) {
          event.stopImmediatePropagation();
          this.focus();
          this._filtering = true;
          this._previousValue = undefined;
          this.change(undefined);
          this._filtering = false;
          this.selectionService.resetSelection([]);
      }
      /**
       * @hidden
       */
      writeValue(value) {
          if (value === null && this._state & InternalState.SetInitial) {
              return;
          }
          this._state |= InternalState.UseModel;
          this.text = "";
          this._modelValue = value;
          this.setState();
      }
      /**
       * @hidden
       */
      registerOnChange(fn) {
          this.onChangeCallback = fn;
      }
      /**
       * @hidden
       */
      registerOnTouched(fn) {
          this.onTouchedCallback = fn;
      }
      /**
       * @hidden
       */
      setDisabledState(isDisabled) {
          this.disabled = isDisabled;
      }
      /**
       * @hidden
       */
      popupOpened() {
          this.popupWidth = this.width.max;
          this.popupMinWidth = this.width.min;
      }
      /**
       * @hidden
       */
      get buttonClasses() {
          return {
              [this.iconClass]: !this.loading && this.iconClass,
              'k-i-arrow-s': !this.loading && !this.iconClass,
              'k-i-loading': this.loading,
              'k-icon': true
          };
      }
      /**
       * @hidden
       */
      onResize() {
          if (this._open) {
              const popupWrapper = this.popupRef.popupElement;
              const { min, max } = this.width;
              popupWrapper.style.minWidth = min;
              popupWrapper.style.width = max;
          }
      }
      verifySettings(newValue) {
          const valueOrText = !isPresent(this.valueField) !== !isPresent(this.textField);
          if (!isDevMode()) {
              return;
          }
          if (this.valuePrimitive === true && isPresent(newValue) && typeof newValue === "object") {
              throw new Error(ComboBoxMessages.primitive);
          }
          if (this.valuePrimitive === false && isPresent(newValue) && typeof newValue !== "object") {
              throw new Error(ComboBoxMessages.object);
          }
          if (valueOrText) {
              throw new Error(ComboBoxMessages.textAndValue);
          }
      }
      resolveState() {
          const primitiveValue = getter(this._modelValue, this.valueField, true);
          let existing;
          let state;
          if (this._filtering) {
              state = InternalState.UseFilter;
          }
          else {
              state = this.data.length ? InternalState.UseExisting : (this.allowCustom ? InternalState.UseCustom : InternalState.UseEmpty);
          }
          if (state === InternalState.UseExisting) {
              existing = resolveValue({ data: this.data, value: primitiveValue, valueField: this.valueField });
              if (existing.dataItem !== undefined) {
                  state = InternalState.UseExisting;
              }
              else {
                  state = this.allowCustom ? InternalState.UseCustom : InternalState.UseEmpty;
              }
          }
          state |= this._state & InternalState.SetInitial | this._state & InternalState.UseModel;
          this._state = state;
          return existing;
      }
      setState() {
          let resolved = this.resolveState();
          let newState = { value: undefined, text: undefined, dataItem: undefined, selected: [-1] };
          if (this._state & InternalState.UseFilter) {
              return;
          }
          else if (this._state & InternalState.UseEmpty) {
              newState.selected = undefined;
              newState.value = undefined;
              newState.text = undefined;
              newState.dataItem = undefined;
          }
          else if (this._state & InternalState.UseExisting) {
              newState.value = isPresent(this._modelValue) ? this._modelValue : resolved.dataItem;
              newState.text = resolved.dataItem;
              newState.dataItem = resolved.dataItem;
              newState.selected = resolved.selected;
          }
          else if (this._state & InternalState.UseCustom) {
              newState.value = this._modelValue;
              newState.text = this._modelValue;
              newState.dataItem = this._modelValue;
          }
          if (this._state & InternalState.UseModel) {
              this._state &= ~InternalState.UseModel;
              this._previousValue = newState.dataItem;
          }
          else {
              this._previousValue = this.dataItem;
          }
          this._state &= ~InternalState.SetInitial;
          this.text = newState.text;
          this.value = this.valuePrimitive ? getter(newState.value, this.valueField, true) : newState.value;
          this.dataItem = newState.dataItem;
          this.selectionService.resetSelection(newState.selected);
      }
      search(text) {
          const index = this.data.findIndex(item => {
              let itemText = getter(item, this.textField);
              itemText = itemText === undefined ? "" : itemText.toString().toLowerCase();
              return itemText.startsWith(text.toLowerCase());
          });
          this.selectionService.focused = index;
          this.suggestedText = getter(this.data[index], this.textField);
      }
      /**
       * @hidden
       */
      getSuggestion() {
          const hasSelected = !!this.selectionService.selected.length;
          const shouldSuggest = this.suggest && !this.backspacePressed && this.suggestedText && this.text;
          if (!hasSelected && shouldSuggest && this.suggestedText.toLowerCase().startsWith(this.text.toLowerCase())) {
              return this.suggestedText;
          }
          else {
              this.suggestedText = undefined;
          }
      }
      navigate(index) {
          if (this.data.length === 0) {
              return;
          }
          this.text = this.data[index];
          this.selectionService.select(index);
      }
      /**
       * @hidden
       */
      handleNavigate(event) {
          const hasSelected = isPresent(this.selectionService.selected[0]);
          const focused = isNaN(this.selectionService.focused) ? 0 : this.selectionService.focused;
          let offset = 0;
          if (this.disabled || this.readonly) {
              return;
          }
          if (event.keyCode === Keys.home || event.keyCode === Keys.end) {
              return;
          }
          if (!hasSelected) {
              if (event.keyCode === Keys.down) {
                  offset = -1;
              }
              else if (event.keyCode === Keys.up) {
                  offset = 1;
              }
          }
          let current = offset + focused;
          if (current < -1) {
              current = -1;
          }
          const action = this.navigationService.process({
              current: current,
              max: this.data.length - 1,
              min: 0,
              originalEvent: event
          });
          if (action !== NavigationAction.Undefined &&
              action !== NavigationAction.Left &&
              action !== NavigationAction.Right &&
              action !== NavigationAction.Backspace &&
              action !== NavigationAction.Delete &&
              ((action === NavigationAction.Enter && this.popupOpen) || action !== NavigationAction.Enter)) {
              event.preventDefault();
          }
      }
      confirmSelection() {
          const focused = this.selectionService.focused;
          const previousText = getter(this._previousValue, this.textField) || "";
          const hasChange = this.text !== previousText;
          let isCustom = false;
          this._filtering = false;
          if (this.allowCustom) {
              isCustom = this.text !== getter(this.data[focused], this.textField);
          }
          else {
              isCustom = focused === -1 || focused === undefined;
          }
          if (!isCustom && this.popupOpen) {
              this.selectionService.select(focused);
              this.change(this.data[focused] || this.text, isCustom);
              return;
          }
          if (hasChange) {
              this.change(this.text, isCustom);
          }
          else {
              this.popupOpen = false;
          }
      }
      /**
       * @hidden
       */
      handleBlur() {
          this._filtering = false;
          const currentText = this.searchbar.value; // The value is updated, but the Angular `change` event is not emitted yet and `this.text` is not updated. Fails on suggested text.
          if (!currentText && !isPresent(this._previousValue)) {
              this.popupOpen = false;
              this.isFocused = false;
              this.onBlur.emit();
              this.onTouchedCallback();
              return;
          }
          const focused = this.selectionService.focused;
          let itemText;
          if (focused !== -1 && focused !== undefined) {
              itemText = getter(this.data[focused], this.textField);
              itemText = itemText === undefined ? "" : itemText.toString().toLowerCase();
          }
          if (itemText === currentText.toLowerCase()) {
              this.selectionService.change(focused);
          }
          else {
              this.change(currentText, true);
          }
          this.popupOpen = false;
          this.isFocused = false;
          this.onBlur.emit();
          this.onTouchedCallback();
      }
      /**
       * @hidden
       */
      searchBarChange(text) {
          const currentTextLength = this.text ? this.text.length : 0;
          this.backspacePressed = (text.length < currentTextLength) ? true : false;
          this.text = text;
          // Reset the selection prior to filter. If a match is present, it will be resolved. If a match is not present, it is not needed.
          this.selectionService.resetSelection([]);
          this.popupOpen = true;
          this._filtering = true;
          if (this.filterable) {
              this.filterChange.emit(text);
          }
          else {
              this.search(text);
          }
      }
      /**
       * @hidden
       */
      handleFocus() {
          this.isFocused = true;
          this.onFocus.emit();
      }
      change(candidate, isCustom = false) {
          this.popupOpen = false;
          if (isCustom) {
              this.customValueSubject.next(candidate);
          }
          else {
              this.valueSubject.next(candidate);
          }
      }
      emitChange() {
          this._modelValue = this.value;
          this._previousValue = this._state & InternalState.UseCustom ? this.value : this.dataItem;
          this.selectionSubject.next(this.data.findIndex((element) => {
              return getter(element, this.valueField) === getter(this.value, this.valueField, this.valuePrimitive);
          }));
          this.onChangeCallback(this.value);
          this.valueChange.emit(this.value);
      }
      /**
       * @hidden
       */
      togglePopup() {
          if (!this.touchEnabled) {
              this.searchbar.focus();
          }
          if (this.popupOpen) {
              this.confirmSelection();
              this.popupOpen = false;
          }
          else {
              this.popupOpen = true;
          }
      }
      get listContainerClasses() {
          const containerClasses = ['k-list-container', 'k-reset'];
          if (this.popupSettings.popupClass) {
              containerClasses.push(this.popupSettings.popupClass);
          }
          return containerClasses;
      }
      _toggle(open) {
          this._open = open;
          if (this.popupRef) {
              this.popupRef.popupElement
                  .removeEventListener('mousedown', this.popupMouseDownHandler);
              this.popupRef.close();
              this.popupRef = null;
          }
          if (this._open) {
              this.popupRef = this.popupService.open({
                  anchor: this.wrapper,
                  animate: this.popupSettings.animate,
                  appendTo: this.appendTo,
                  content: this.popupTemplate,
                  popupClass: this.listContainerClasses,
                  positionMode: 'absolute'
              });
              const popupWrapper = this.popupRef.popupElement;
              const { min, max } = this.width;
              popupWrapper.addEventListener('mousedown', this.popupMouseDownHandler);
              popupWrapper.style.minWidth = min;
              popupWrapper.style.width = max;
              popupWrapper.style.height = this.height;
              popupWrapper.setAttribute("dir", this.direction);
              this.popupRef.popupAnchorViewportLeave.subscribe(() => this.popupOpen = false);
          }
      }
      setMessages() {
          this._zone.runOutsideAngular(() => {
              clearTimeout(this.messagesTimeout);
              this.messagesTimeout = setTimeout(() => {
                  this.noDataText = this.localization.get('noDataText');
                  this.cdr.detectChanges();
              });
          });
      }
  }
  ComboBoxComponent.decorators = [
      { type: Component, args: [{
                  exportAs: 'kendoComboBox',
                  providers: [
                      COMBOBOX_VALUE_ACCESSOR,
                      SelectionService,
                      NavigationService,
                      DisabledItemsService,
                      LocalizationService,
                      {
                          provide: L10N_PREFIX,
                          useValue: 'kendo.combobox'
                      },
                      {
                          provide: FilterableDropDownComponentBase, useExisting: forwardRef(() => ComboBoxComponent)
                      }
                  ],
                  selector: 'kendo-combobox',
                  template: `
          <ng-container kendoComboBoxLocalizedMessages
              i18n-noDataText="kendo.combobox.noDataText|The text displayed in the popup when there are no items"
              noDataText="NO DATA FOUND"
          >
          </ng-container>
          <span #wrapper unselectable="on" [ngClass]="wrapperClasses()">
            <kendo-searchbar #searchbar
                [role]="'listbox'"
                [id]="focusableId"
                [listId]="listBoxId"
                [activeDescendant]="activeDescendant"
                [noDataLabel]="noDataLabel"
                [userInput]="text"
                [suggestedText]="getSuggestion()"
                [disabled]="disabled"
                [readonly]="readonly"
                [tabIndex]="tabIndex"
                [popupOpen]="popupOpen"
                [placeholder]="placeholder"
                (onNavigate)="handleNavigate($event)"
                (valueChange)="searchBarChange($event)"
                (onBlur)="handleBlur()"
                (onFocus)="handleFocus()"
            ></kendo-searchbar>
            <span *ngIf="!loading && !readonly && (clearButton && text?.length)" class="k-icon k-clear-value k-i-close" title="clear" role="button" tabindex="-1" (click)="clearValue($event)" (mousedown)="$event.preventDefault()"></span>
            <span unselectable="on"
                [ngClass]="{ 'k-select': true }"
                (click)="togglePopup()"
                (mousedown)="$event.preventDefault()" >
                <span [ngClass]="buttonClasses"></span>
            </span>
            <ng-template #popupTemplate>
                <!--header template-->
                <ng-template *ngIf="headerTemplate"
                    [templateContext]="{
                        templateRef: headerTemplate.templateRef
                    }">
                </ng-template>
                <!--list-->
                <kendo-list
                    [id]="listBoxId"
                    [optionPrefix]="optionPrefix"
                    [data]="data"
                    [textField]="textField"
                    [valueField]="valueField"
                    [template]="template"
                    [height]="listHeight"
                    [show]="popupOpen"
                >
                </kendo-list>
                <!--no-data template-->
                <div class="k-nodata" *ngIf="data.length === 0">
                    <ng-template [ngIf]="noDataTemplate"
                        [templateContext]="{
                            templateRef: noDataTemplate ? noDataTemplate.templateRef : undefined
                        }">
                    </ng-template>
                    <ng-template [ngIf]="!noDataTemplate">
                        <div>{{ noDataText }}</div>
                    </ng-template>
                </div>
                <!--footer template-->
                <ng-template *ngIf="footerTemplate"
                    [templateContext]="{
                        templateRef: footerTemplate.templateRef
                    }">
                </ng-template>
            </ng-template>
          </span>
          <ng-template [ngIf]="popupOpen">
              <kendo-resize-sensor (resize)="onResize()"></kendo-resize-sensor>
          </ng-template>
          <ng-container #container></ng-container>
    `
              },] },
  ];
  /** @nocollapse */
  ComboBoxComponent.ctorParameters = () => [
      { type: LocalizationService, },
      { type: PopupService, },
      { type: SelectionService, },
      { type: NavigationService, },
      { type: NgZone, },
      { type: ChangeDetectorRef, },
      { type: Renderer2, },
      { type: ElementRef, },
      { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [TOUCH_ENABLED,] },] },
  ];
  ComboBoxComponent.propDecorators = {
      'focusableId': [{ type: Input },],
      'allowCustom': [{ type: Input },],
      'data': [{ type: Input },],
      'value': [{ type: Input },],
      'textField': [{ type: Input },],
      'valueField': [{ type: Input },],
      'valuePrimitive': [{ type: Input },],
      'valueNormalizer': [{ type: Input },],
      'placeholder': [{ type: Input },],
      'popupSettings': [{ type: Input },],
      'listHeight': [{ type: Input },],
      'iconClass': [{ type: Input },],
      'loading': [{ type: Input },],
      'suggest': [{ type: Input },],
      'clearButton': [{ type: Input },],
      'disabled': [{ type: Input },],
      'readonly': [{ type: Input },],
      'tabindex': [{ type: Input },],
      'tabIndex': [{ type: Input, args: ["tabIndex",] },],
      'filterable': [{ type: Input },],
      'valueChange': [{ type: Output },],
      'selectionChange': [{ type: Output },],
      'filterChange': [{ type: Output },],
      'open': [{ type: Output },],
      'close': [{ type: Output },],
      'onFocus': [{ type: Output, args: ['focus',] },],
      'onBlur': [{ type: Output, args: ['blur',] },],
      'template': [{ type: ContentChild, args: [ItemTemplateDirective,] },],
      'headerTemplate': [{ type: ContentChild, args: [HeaderTemplateDirective,] },],
      'footerTemplate': [{ type: ContentChild, args: [FooterTemplateDirective,] },],
      'noDataTemplate': [{ type: ContentChild, args: [NoDataTemplateDirective,] },],
      'container': [{ type: ViewChild, args: ['container', { read: ViewContainerRef },] },],
      'popupTemplate': [{ type: ViewChild, args: ['popupTemplate',] },],
      'searchbar': [{ type: ViewChild, args: [SearchBarComponent,] },],
      'widgetClasses': [{ type: HostBinding, args: ['class.k-widget',] }, { type: HostBinding, args: ['class.k-combobox',] }, { type: HostBinding, args: ['class.k-header',] },],
      'clearable': [{ type: HostBinding, args: ['class.k-combobox-clearable',] },],
      'dir': [{ type: HostBinding, args: ['attr.dir',] },],
  };
  