text: import { Directive, ElementRef, EventEmitter, HostListener, Input, Output, Renderer, ViewContainerRef } from '@angular/core';
  import { NgControl } from '@angular/forms';
  import { TypeaheadContainerComponent } from './typeahead-container.component';
  import { getValueFromObject, latinize, tokenize } from './typeahead-utils';
  import { Observable } from 'rxjs/Observable';
  import 'rxjs/add/observable/from';
  import 'rxjs/add/operator/debounceTime';
  import 'rxjs/add/operator/filter';
  import 'rxjs/add/operator/map';
  import 'rxjs/add/operator/mergeMap';
  import 'rxjs/add/operator/toArray';
  import { TypeaheadMatch } from './typeahead-match.class';
  import { ComponentLoaderFactory } from '../component-loader';
  var TypeaheadDirective = (function () {
      function TypeaheadDirective(control, viewContainerRef, element, renderer, cis) {
          /** minimal no of characters that needs to be entered before typeahead kicks-in. When set to 0, typeahead shows on focus with full list of options (limited as normal by typeaheadOptionsLimit) */
          this.typeaheadMinLength = void 0;
          /** should be used only in case of typeahead attribute is array. If true - loading of options will be async, otherwise - sync. true make sense if options array is large. */
          this.typeaheadAsync = void 0;
          /** match latin symbols. If true the word súper would match super and vice versa. */
          this.typeaheadLatinize = true;
          /** break words with spaces. If true the text "exact phrase" here match would match with match exact phrase here but not with phrase here exact match (kind of "google style"). */
          this.typeaheadSingleWords = true;
          /** should be used only in case typeaheadSingleWords attribute is true. Sets the word delimiter to break words. Defaults to space. */
          this.typeaheadWordDelimiters = ' ';
          /** should be used only in case typeaheadSingleWords attribute is true. Sets the word delimiter to match exact phrase. Defaults to simple and double quotes. */
          this.typeaheadPhraseDelimiters = '\'"';
          /** fired when 'busy' state of this component was changed, fired on async mode only, returns boolean */
          this.typeaheadLoading = new EventEmitter();
          /** fired on every key event and returns true in case of matches are not detected */
          this.typeaheadNoResults = new EventEmitter();
          /** fired when option was selected, return object with data of this option */
          this.typeaheadOnSelect = new EventEmitter();
          /** fired when blur event occurres. returns the active item */
          this.typeaheadOnBlur = new EventEmitter();
          /** This attribute indicates that the dropdown should be opened upwards */
          this.dropup = false;
          this.isTypeaheadOptionsListActive = false;
          this.keyUpEventEmitter = new EventEmitter();
          this.placement = 'bottom-left';
          this._subscriptions = [];
          this.element = element;
          this.ngControl = control;
          this.viewContainerRef = viewContainerRef;
          this.renderer = renderer;
          this._typeahead = cis
              .createLoader(element, viewContainerRef, renderer);
      }
      TypeaheadDirective.prototype.onChange = function (e) {
          if (this._container) {
              // esc
              if (e.keyCode === 27) {
                  this.hide();
                  return;
              }
              // up
              if (e.keyCode === 38) {
                  this._container.prevActiveMatch();
                  return;
              }
              // down
              if (e.keyCode === 40) {
                  this._container.nextActiveMatch();
                  return;
              }
              // enter
              if (e.keyCode === 13) {
                  this._container.selectActiveMatch();
                  return;
              }
          }
          // For `<input>`s, use the `value` property. For others that don't have a
          // `value` (such as `<span contenteditable="true">`), use either
          // `textContent` or `innerText` (depending on which one is supported, i.e.
          // Firefox or IE).
          var value = e.target.value !== undefined
              ? e.target.value
              : e.target.textContent !== undefined
                  ? e.target.textContent
                  : e.target.innerText;
          if (value != null && value.trim().length >= this.typeaheadMinLength) {
              this.typeaheadLoading.emit(true);
              this.keyUpEventEmitter.emit(e.target.value);
          }
          else {
              this.typeaheadLoading.emit(false);
              this.typeaheadNoResults.emit(false);
              this.hide();
          }
      };
      TypeaheadDirective.prototype.onFocus = function () {
          if (this.typeaheadMinLength === 0) {
              this.typeaheadLoading.emit(true);
              this.keyUpEventEmitter.emit('');
          }
      };
      TypeaheadDirective.prototype.onBlur = function () {
          if (this._container && !this._container.isFocused) {
              this.typeaheadOnBlur.emit(this._container.active);
              this.hide();
          }
      };
      TypeaheadDirective.prototype.onKeydown = function (e) {
          // no container - no problems
          if (!this._container) {
              return;
          }
          // if items is visible - prevent form submition
          if (e.keyCode === 13) {
              e.preventDefault();
              return;
          }
      };
      TypeaheadDirective.prototype.ngOnInit = function () {
          this.typeaheadOptionsLimit = this.typeaheadOptionsLimit || 20;
          this.typeaheadMinLength = this.typeaheadMinLength === void 0
              ? 1
              : this.typeaheadMinLength;
          this.typeaheadWaitMs = this.typeaheadWaitMs || 0;
          // async should be false in case of array
          if (this.typeaheadAsync === undefined && !(this.typeahead instanceof Observable)) {
              this.typeaheadAsync = false;
          }
          if (this.typeahead instanceof Observable) {
              this.typeaheadAsync = true;
          }
          if (this.typeaheadAsync) {
              this.asyncActions();
          }
          else {
              this.syncActions();
          }
      };
      TypeaheadDirective.prototype.changeModel = function (match) {
          var valueStr = match.value;
          this.ngControl.viewToModelUpdate(valueStr);
          this.ngControl.control.setValue(valueStr);
          this.hide();
      };
      Object.defineProperty(TypeaheadDirective.prototype, "matches", {
          get: function () {
              return this._matches;
          },
          enumerable: true,
          configurable: true
      });
      TypeaheadDirective.prototype.show = function () {
          this._typeahead
              .attach(TypeaheadContainerComponent)
              .to(this.container)
              .position({ attachment: (this.dropup ? 'top' : 'bottom') + " left" })
              .show({
              typeaheadRef: this,
              placement: this.placement,
              animation: false,
              dropup: this.dropup
          });
          this._container = this._typeahead.instance;
          this._container.parent = this;
          // This improves the speed as it won't have to be done for each list item
          var normalizedQuery = (this.typeaheadLatinize
              ? latinize(this.ngControl.control.value)
              : this.ngControl.control.value).toString()
              .toLowerCase();
          this._container.query = this.typeaheadSingleWords
              ? tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
              : normalizedQuery;
          this._container.matches = this._matches;
          this.element.nativeElement.focus();
      };
      TypeaheadDirective.prototype.hide = function () {
          if (this._typeahead.isShown) {
              this._typeahead.hide();
              this._container = null;
          }
      };
      TypeaheadDirective.prototype.ngOnDestroy = function () {
          // clean up subscriptions
          for (var _i = 0, _a = this._subscriptions; _i < _a.length; _i++) {
              var sub = _a[_i];
              sub.unsubscribe();
          }
          this._typeahead.dispose();
      };
      TypeaheadDirective.prototype.asyncActions = function () {
          var _this = this;
          this._subscriptions.push(this.keyUpEventEmitter
              .debounceTime(this.typeaheadWaitMs)
              .mergeMap(function () { return _this.typeahead; })
              .subscribe(function (matches) {
              _this.finalizeAsyncCall(matches);
          }, function (err) {
              console.error(err);
          }));
      };
      TypeaheadDirective.prototype.syncActions = function () {
          var _this = this;
          this._subscriptions.push(this.keyUpEventEmitter
              .debounceTime(this.typeaheadWaitMs)
              .mergeMap(function (value) {
              var normalizedQuery = _this.normalizeQuery(value);
              return Observable.from(_this.typeahead)
                  .filter(function (option) {
                  return option && _this.testMatch(_this.normalizeOption(option), normalizedQuery);
              })
                  .toArray();
          })
              .subscribe(function (matches) {
              _this.finalizeAsyncCall(matches);
          }, function (err) {
              console.error(err);
          }));
      };
      TypeaheadDirective.prototype.normalizeOption = function (option) {
          var optionValue = getValueFromObject(option, this.typeaheadOptionField);
          var normalizedOption = this.typeaheadLatinize
              ? latinize(optionValue)
              : optionValue;
          return normalizedOption.toLowerCase();
      };
      TypeaheadDirective.prototype.normalizeQuery = function (value) {
          // If singleWords, break model here to not be doing extra work on each
          // iteration
          var normalizedQuery = (this.typeaheadLatinize ? latinize(value) : value)
              .toString()
              .toLowerCase();
          normalizedQuery = this.typeaheadSingleWords
              ? tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
              : normalizedQuery;
          return normalizedQuery;
      };
      TypeaheadDirective.prototype.testMatch = function (match, test) {
          var spaceLength;
          if (typeof test === 'object') {
              spaceLength = test.length;
              for (var i = 0; i < spaceLength; i += 1) {
                  if (test[i].length > 0 && match.indexOf(test[i]) < 0) {
                      return false;
                  }
              }
              return true;
          }
          else {
              return match.indexOf(test) >= 0;
          }
      };
      TypeaheadDirective.prototype.finalizeAsyncCall = function (matches) {
          this.prepareMatches(matches);
          this.typeaheadLoading.emit(false);
          this.typeaheadNoResults.emit(!this.hasMatches());
          if (!this.hasMatches()) {
              this.hide();
              return;
          }
          if (this._container) {
              // This improves the speed as it won't have to be done for each list item
              var normalizedQuery = (this.typeaheadLatinize
                  ? latinize(this.ngControl.control.value)
                  : this.ngControl.control.value).toString()
                  .toLowerCase();
              this._container.query = this.typeaheadSingleWords
                  ? tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
                  : normalizedQuery;
              this._container.matches = this._matches;
          }
          else {
              this.show();
          }
      };
      TypeaheadDirective.prototype.prepareMatches = function (options) {
          var _this = this;
          var limited = options.slice(0, this.typeaheadOptionsLimit);
          if (this.typeaheadGroupField) {
              var matches_1 = [];
              // extract all group names
              var groups = limited
                  .map(function (option) { return getValueFromObject(option, _this.typeaheadGroupField); })
                  .filter(function (v, i, a) { return a.indexOf(v) === i; });
              groups.forEach(function (group) {
                  // add group header to array of matches
                  matches_1.push(new TypeaheadMatch(group, group, true));
                  // add each item of group to array of matches
                  matches_1 = matches_1.concat(limited
                      .filter(function (option) { return getValueFromObject(option, _this.typeaheadGroupField) === group; })
                      .map(function (option) { return new TypeaheadMatch(option, getValueFromObject(option, _this.typeaheadOptionField)); }));
              });
              this._matches = matches_1;
          }
          else {
              this._matches = limited.map(function (option) { return new TypeaheadMatch(option, getValueFromObject(option, _this.typeaheadOptionField)); });
          }
      };
      TypeaheadDirective.prototype.hasMatches = function () {
          return this._matches.length > 0;
      };
      TypeaheadDirective.decorators = [
          { type: Directive, args: [{ selector: '[typeahead]', exportAs: 'bs-typeahead' },] },
      ];
      /** @nocollapse */
      TypeaheadDirective.ctorParameters = function () { return [
          { type: NgControl, },
          { type: ViewContainerRef, },
          { type: ElementRef, },
          { type: Renderer, },
          { type: ComponentLoaderFactory, },
      ]; };
      TypeaheadDirective.propDecorators = {
          'typeahead': [{ type: Input },],
          'typeaheadMinLength': [{ type: Input },],
          'typeaheadWaitMs': [{ type: Input },],
          'typeaheadOptionsLimit': [{ type: Input },],
          'typeaheadOptionField': [{ type: Input },],
          'typeaheadGroupField': [{ type: Input },],
          'typeaheadAsync': [{ type: Input },],
          'typeaheadLatinize': [{ type: Input },],
          'typeaheadSingleWords': [{ type: Input },],
          'typeaheadWordDelimiters': [{ type: Input },],
          'typeaheadPhraseDelimiters': [{ type: Input },],
          'typeaheadItemTemplate': [{ type: Input },],
          'optionsListTemplate': [{ type: Input },],
          'typeaheadLoading': [{ type: Output },],
          'typeaheadNoResults': [{ type: Output },],
          'typeaheadOnSelect': [{ type: Output },],
          'typeaheadOnBlur': [{ type: Output },],
          'container': [{ type: Input },],
          'dropup': [{ type: Input },],
          'onChange': [{ type: HostListener, args: ['keyup', ['$event'],] },],
          'onFocus': [{ type: HostListener, args: ['focus',] },],
          'onBlur': [{ type: HostListener, args: ['blur',] },],
          'onKeydown': [{ type: HostListener, args: ['keydown', ['$event'],] },],
      };
      return TypeaheadDirective;
  }());
  export { TypeaheadDirective };
  //# sourceMappingURL=typeahead.directive.js.map