<commit_before>/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {
  Component,
  Directive,
  Input,
  ElementRef,
  ViewContainerRef,
  NgZone,
  Optional,
  OnDestroy,
  Renderer2,
  ChangeDetectorRef,
  ViewEncapsulation,
} from '@angular/core';
import {
  style,
  trigger,
  state,
  transition,
  animate,
  AnimationEvent,
} from '@angular/animations';
import {
  Overlay,
  OverlayState,
  OverlayRef,
  ComponentPortal,
  OverlayConnectionPosition,
  OriginConnectionPosition,
} from '../core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Directionality} from '../core/bidi/index';
import {Platform} from '../core/platform/index';
import {first} from '../core/rxjs/index';
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
import {coerceBooleanProperty} from '@angular/cdk';

export type TooltipPosition = 'left' | 'right' | 'above' | 'below' | 'before' | 'after';

/** Time in ms to delay before changing the tooltip visibility to hidden */
export const TOUCHEND_HIDE_DELAY = 1500;

/** Time in ms to throttle repositioning after scroll events. */
export const SCROLL_THROTTLE_MS = 20;

/** Creates an error to be thrown if the user supplied an invalid tooltip position. */
export function getMdTooltipInvalidPositionError(position: string) {
  return Error(`Tooltip position "${position}" is invalid.`);
}

/**
 * Directive that attaches a material design tooltip to the host element. Animates the showing and
 * hiding of a tooltip provided position (defaults to below the element).
 *
 * https://material.google.com/components/tooltips.html
 */
@Directive({
  selector: '[md-tooltip], [mdTooltip], [mat-tooltip], [matTooltip]',
  host: {
    '(longpress)': 'show()',
    '(touchend)': 'hide(' + TOUCHEND_HIDE_DELAY + ')',
  },
  exportAs: 'mdTooltip',
})
export class MdTooltip implements OnDestroy {
  _overlayRef: OverlayRef | null;
  _tooltipInstance: TooltipComponent | null;

  private _position: TooltipPosition = 'below';
  private _disabled: boolean = false;
  private _tooltipClass: string|string[]|Set<string>|{[key: string]: any};

  /** Allows the user to define the position of the tooltip relative to the parent element */
  @Input('mdTooltipPosition')
  get position(): TooltipPosition { return this._position; }
  set position(value: TooltipPosition) {
    if (value !== this._position) {
      this._position = value;

      // TODO(andrewjs): When the overlay's position can be dynamically changed, do not destroy
      // the tooltip.
      if (this._tooltipInstance) {
        this._disposeTooltip();
      }
    }
  }

  /** Disables the display of the tooltip. */
  @Input('mdTooltipDisabled')
  get disabled(): boolean { return this._disabled; }
  set disabled(value) {
    this._disabled = coerceBooleanProperty(value);

    // If tooltip is disabled, hide immediately.
    if (this._disabled) {
      this.hide(0);
    }
  }

  /** @deprecated */
  @Input('tooltip-position')
  get _positionDeprecated(): TooltipPosition { return this._position; }
  set _positionDeprecated(value: TooltipPosition) { this._position = value; }

  /** The default delay in ms before showing the tooltip after show is called */
  @Input('mdTooltipShowDelay') showDelay = 0;

  /** The default delay in ms before hiding the tooltip after hide is called */
  @Input('mdTooltipHideDelay') hideDelay = 0;

  private _message: string;

  /** The message to be displayed in the tooltip */
  @Input('mdTooltip') get message() { return this._message; }
  set message(value: string) {
    this._message = value;
    this._setTooltipMessage(this._message);
  }

  /** Classes to be passed to the tooltip. Supports the same syntax as `ngClass`. */
  @Input('mdTooltipClass')
  get tooltipClass() { return this._tooltipClass; }
  set tooltipClass(value: string|string[]|Set<string>|{[key: string]: any}) {
    this._tooltipClass = value;
    if (this._tooltipInstance) {
      this._setTooltipClass(this._tooltipClass);
    }
  }

  /** @deprecated */
  @Input('md-tooltip')
  get _deprecatedMessage(): string { return this.message; }
  set _deprecatedMessage(v: string) { this.message = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltip')
  get _matMessage() { return this.message; }
  set _matMessage(v) { this.message = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipPosition')
  get _matPosition() { return this.position; }
  set _matPosition(v) { this.position = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipDisabled')
  get _matDisabled() { return this.disabled; }
  set _matDisabled(v) { this.disabled = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipHideDelay')
  get _matHideDelay() { return this.hideDelay; }
  set _matHideDelay(v) { this.hideDelay = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipShowDelay')
  get _matShowDelay() { return this.showDelay; }
  set _matShowDelay(v) { this.showDelay = v; }

  // Properties with `mat-` prefix for nonconflict mode.
  @Input('matTooltipClass')
  get _matClass() { return this.tooltipClass; }
  set _matClass(v) { this.tooltipClass = v; }

  constructor(
    private _overlay: Overlay,
    private _elementRef: ElementRef,
    private _scrollDispatcher: ScrollDispatcher,
    private _viewContainerRef: ViewContainerRef,
    private _ngZone: NgZone,
    private _renderer: Renderer2,
    private _platform: Platform,
    @Optional() private _dir: Directionality) {

    // The mouse events shouldn't be bound on iOS devices, because
    // they can prevent the first tap from firing its click event.
    if (!_platform.IOS) {
      _renderer.listen(_elementRef.nativeElement, 'mouseenter', () => this.show());
      _renderer.listen(_elementRef.nativeElement, 'mouseleave', () => this.hide());
    }
  }

  /**
   * Dispose the tooltip when destroyed.
   */
  ngOnDestroy() {
    if (this._tooltipInstance) {
      this._disposeTooltip();
    }
  }

  /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */
  show(delay: number = this.showDelay): void {
    if (this.disabled || !this._message || !this._message.trim()) { return; }

    if (!this._tooltipInstance) {
      this._createTooltip();
    }

    this._setTooltipClass(this._tooltipClass);
    this._setTooltipMessage(this._message);
    this._tooltipInstance!.show(this._position, delay);
  }

  /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */
  hide(delay: number = this.hideDelay): void {
    if (this._tooltipInstance) {
      this._tooltipInstance.hide(delay);
    }
  }

  /** Shows/hides the tooltip */<commit_msg>fix(tooltip): remove native event listener on component destroy (#5144)<commit_after>/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {
  Component,
  Directive,
  Input,
  ElementRef,
  ViewContainerRef,
  NgZone,
  Optional,
  OnDestroy,
  Renderer2,
  ChangeDetectorRef,
  ViewEncapsulation,
} from '@angular/core';
import {
  style,
  trigger,
  state,
  transition,
  animate,
  AnimationEvent,
} from '@angular/animations';
import {
  Overlay,
  OverlayState,
  OverlayRef,
  ComponentPortal,
  OverlayConnectionPosition,
  OriginConnectionPosition,
} from '../core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Directionality} from '../core/bidi/index';
import {Platform} from '../core/platform/index';
import {first} from '../core/rxjs/index';
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
import {coerceBooleanProperty} from '@angular/cdk';

export type TooltipPosition = 'left' | 'right' | 'above' | 'below' | 'before' | 'after';

/** Time in ms to delay before changing the tooltip visibility to hidden */
export const TOUCHEND_HIDE_DELAY = 1500;

/** Time in ms to throttle repositioning after scroll events. */
export const SCROLL_THROTTLE_MS = 20;

/** Creates an error to be thrown if the user supplied an invalid tooltip position. */
export function getMdTooltipInvalidPositionError(position: string) {
  return Error(`Tooltip position "${position}" is invalid.`);
}

/**
 * Directive that attaches a material design tooltip to the host element. Animates the showing and
 * hiding of a tooltip provided position (defaults to below the element).
 *
 * https://material.google.com/components/tooltips.html
 */
@Directive({
  selector: '[md-tooltip], [mdTooltip], [mat-tooltip], [matTooltip]',
  host: {
    '(longpress)': 'show()',
    '(touchend)': 'hide(' + TOUCHEND_HIDE_DELAY + ')',
  },
  exportAs: 'mdTooltip',
})
export class MdTooltip implements OnDestroy {
  _overlayRef: OverlayRef | null;
  _tooltipInstance: TooltipComponent | null;

  private _position: TooltipPosition = 'below';
  private _disabled: boolean = false;
  private _tooltipClass: string|string[]|Set<string>|{[key: string]: any};

  /** Allows the user to define the position of the tooltip relative to the parent element */
  @Input('mdTooltipPosition')
  get position(): TooltipPosition { return this._position; }
  set position(value: TooltipPosition) {
    if (value !== this._position) {
      this._position = value;

      // TODO(andrewjs): When the overlay's position can be dynamically changed, do not destroy
      // the tooltip.
      if (this._tooltipInstance) {
        this._disposeTooltip();
      }
    }
  }

  /** Disables the display of the tooltip. */
  @Input('mdTooltipDisabled')
  get disabled(): boolean { return this._disabled; }
  set disabled(value) {
    this._disabled = coerceBooleanProperty(value);

    // If tooltip is disabled, hide immediately.
    if (this._disabled) {
      this.hide(0);
    }
  }

  /** @deprecated */
  @Input('tooltip-position')
  get _positionDeprecated(): TooltipPosition { return this._position; }
  set _positionDeprecated(value: TooltipPosition) { this._position = value; }

  /** The default delay in ms before showing the tooltip after show is called */
  @Input('mdTooltipShowDelay') showDelay = 0;

  /** The default delay in ms before hiding the tooltip after hide is called */
  @Input('mdTooltipHideDelay') hideDelay = 0;

  private _message: string;

  /** The message to be displayed in the tooltip */
  @Input('mdTooltip') get message() { return this._message; }
  set message(value: string) {
    this._message = value;
    this._setTooltipMessage(this._message);
  }

  /** Classes to be passed to the tooltip. Supports the same syntax as `ngClass`. */
  @Input('mdTooltipClass')
  get tooltipClass() { return this._tooltipClass; }
  set tooltipClass(value: string|string[]|Set<string>|{[key: string]: any}) {
    this._tooltipClass = value;
    if (this._tooltipInstance) {
      this._setTooltipClass(this._tooltipClass);
    }
  }

  /** @deprecated */
  @Input('md-tooltip')
  get _deprecatedMessage(): string { return this.message; }
  set _deprecatedMessage(v: string) { this.message = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltip')
  get _matMessage() { return this.message; }
  set _matMessage(v) { this.message = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipPosition')
  get _matPosition() { return this.position; }
  set _matPosition(v) { this.position = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipDisabled')
  get _matDisabled() { return this.disabled; }
  set _matDisabled(v) { this.disabled = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipHideDelay')
  get _matHideDelay() { return this.hideDelay; }
  set _matHideDelay(v) { this.hideDelay = v; }

  // Properties with `mat-` prefix for noconflict mode.
  @Input('matTooltipShowDelay')
  get _matShowDelay() { return this.showDelay; }
  set _matShowDelay(v) { this.showDelay = v; }

  // Properties with `mat-` prefix for nonconflict mode.
  @Input('matTooltipClass')
  get _matClass() { return this.tooltipClass; }
  set _matClass(v) { this.tooltipClass = v; }

  private _enterListener: Function;
  private _leaveListener: Function;

  constructor(
    private _overlay: Overlay,
    private _elementRef: ElementRef,
    private _scrollDispatcher: ScrollDispatcher,
    private _viewContainerRef: ViewContainerRef,
    private _ngZone: NgZone,
    private _renderer: Renderer2,
    private _platform: Platform,
    @Optional() private _dir: Directionality) {

    // The mouse events shouldn't be bound on iOS devices, because
    // they can prevent the first tap from firing its click event.
    if (!_platform.IOS) {
      this._enterListener =
        _renderer.listen(_elementRef.nativeElement, 'mouseenter', () => this.show());
      this._leaveListener =
        _renderer.listen(_elementRef.nativeElement, 'mouseleave', () => this.hide());
    }
  }

  /**
   * Dispose the tooltip when destroyed.
   */
  ngOnDestroy() {
    if (this._tooltipInstance) {
      this._disposeTooltip();
    }
    // Clean up the event listeners set in the constructor
    if (!this._platform.IOS) {
      this._enterListener();
      this._leaveListener();
    }
  }

  /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */
  show(delay: number = this.showDelay): void {
    if (this.disabled || !this._message || !this._message.trim()) { return; }

    if (!this._tooltipInstance) {
      this._createTooltip();
    }

    this._setTooltipClass(this._tooltipClass);
    this._setTooltipMessage(this._message);
    this._tooltipInstance!.show(this._position, delay);
  }

  /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */
  hide(delay: number = this.hideDelay): void {
    if (this._tooltipInstance) {
      this._tooltipInstance.hide(delay);
    }
  }

  /** Shows/hides the tooltip */
{'fraction_non_alphanumeric': 0.12370916754478398, 'fraction_numerical': 0.002107481559536354, 'mean_word_length': 3.7548430193720774, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3798712', 'n_tokens_mistral': 4330, 'n_tokens_neox': 4264, 'n_words': 1251}