<!DOCTYPE html>
<html>
  <head>
    <title>Angular 2 Tooltip - Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- CSS file -->
    <link rel="stylesheet" type="text/css" href="style.css">
   <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" >

    <!-- IE polyfills, keep the order please -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
    
    <!-- Agular 2 -->
    <script src="https://code.angularjs.org/2.0.0-beta.7/angular2-polyfills.js"></script>
    <script src="https://code.angularjs.org/tools/system.js"></script>
    <script src="https://code.angularjs.org/tools/typescript.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.7/Rx.js"></script>
    <script src="https://code.angularjs.org/2.0.0-beta.7/angular2.dev.js"></script>
     
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js"></script>
   
     
     <!-- Config Agular 2 and Typescript -->
    <script>
      System.config({
        transpiler: 'typescript', 
        typescriptOptions: { emitDecoratorMetadata: true }, 
        packages: {'app': {defaultExtension: 'ts'}} 
      });
      System.import('app/main')
            .then(null, console.error.bind(console));
    </script>

  </head>
 
  <!-- Run the application -->
  <body>
    <h1>Angular 2 Tooltip - Stars</h1> 
    <my-app class="container" style="display: block">Loading Sample...</my-app>
  
    <div style="padding-top:50px">
    <a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Tutorial"> 
     <img src="http://www.angulartypescript.com/wp-content/uploads/2016/03/learn-more-angular-2.png" alt="Smiley face" height="200" width="500">   
    </a>  
        <ul class="nav nav-pills nav-stacked" >
            <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-tutorial/" title="Angular 2 Home"> Angular 2 Tutorial </a></li>
          <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-introduction/">Angular 2 Introduction</a></li>
          <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-architecture/">Angular 2 Architecture</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-annotations/">Angular 2 Annotations</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-getting-started/">Angular 2 Setup</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-hello-world/">Angular 2 Hello World</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-components/">Angular 2 Components</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-template-syntax/">Angular 2 Template Syntax</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-data-binding/">Angular 2 Data Binding</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-forms/">Angular 2 Forms</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-formbuilder-example/">Angular 2 Formbuilder</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-router-example/">Angular 2 Router</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-http-example-typescript/">Angular 2 HTTP</a></li>
      <li><a target="_blank" href="http://www.angulartypescript.com/angular-2-services/">Angular 2 Service</a></li> 
    
        </ul>
    </div> 
  </body>

</html>
<!-- 
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/ 
-->

/* Styles go here */

/**
 * Created by Tareq Boulakjar. from angulartypescript.com
 */
import {bootstrap}  from 'angular2/platform/browser';
import {Angular2Tooltip} from './tooltipe-example';
import {
 enableProdMode,
} from 'angular2/core';

enableProdMode();
bootstrap(Angular2Tooltip);


/*
Copyright 2016 angulartypescript.com. All Rights Reserved.
Everyone can use this source code; don’t forget to indicate the source please:
http://www.angulartypescript.com/ 
*/
import {Directive, TemplateRef, ViewContainerRef, Inject} from 'angular2/core';

export interface IAttribute {
    [key: string]: any;
}

@Directive({
    selector: '[ngTransclude]',
    properties: ['ngTransclude']
})
export class NgTransclude {
    private _ngTransclude: TemplateRef;

    private set ngTransclude(templateRef:TemplateRef) {
        this._ngTransclude = templateRef;
        if (templateRef) {
            this.viewRef.createEmbeddedView(templateRef);
        }
    }

    private get ngTransclude() {
        return this._ngTransclude;
    }

    constructor(@Inject(ViewContainerRef) public viewRef:ViewContainerRef) {
    }
}
import {
    Injectable,
    ElementRef
} from 'angular2/core';
import {IAttribute} from './IAttribute';

export class PositionService {
    private get window():any {
        return window;
    }

    private get document():any {
        return window.document;
    }

    private getStyle(nativeEl:any, cssProp:string):any {
        // IE
        if (nativeEl.currentStyle) {
            return nativeEl.currentStyle[cssProp];
        }

        if (this.window.getComputedStyle) {
            return this.window.getComputedStyle(nativeEl)[cssProp];
        }
        // finally try and get inline style
        return nativeEl.style[cssProp];
    }


    /**
     * Checks if a given element is statically positioned
     * @param nativeEl - raw DOM element
     */
    private isStaticPositioned(nativeEl:any):any {
        return (this.getStyle(nativeEl, 'position') || 'static' ) === 'static';
    }


    /**
     * returns the closest, non-statically positioned parentOffset of a given element
     * @param nativeEl
     */
    private parentOffsetEl(nativeEl:any) {
        let offsetParent = nativeEl.offsetParent || this.document;
        while (offsetParent && offsetParent !== this.document &&
        this.isStaticPositioned(offsetParent)) {
            offsetParent = offsetParent.offsetParent;
        }
        return offsetParent || this.document;
    };

    /**
     * Provides read-only equivalent of jQuery's position function:
     * http://api.jquery.com/position/
     */
    public position(nativeEl:any):{width: number, height: number, top: number, left: number} {
        let elBCR = this.offset(nativeEl);
        let offsetParentBCR = {top: 0, left: 0};
        let offsetParentEl = this.parentOffsetEl(nativeEl);
        if (offsetParentEl !== this.document) {
            offsetParentBCR = this.offset(offsetParentEl);
            offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
            offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
        }

        let boundingClientRect = nativeEl.getBoundingClientRect();
        return {
            width: boundingClientRect.width || nativeEl.offsetWidth,
            height: boundingClientRect.height || nativeEl.offsetHeight,
            top: elBCR.top - offsetParentBCR.top,
            left: elBCR.left - offsetParentBCR.left
        };
    }

    /**
     * Provides read-only equivalent of jQuery's offset function:
     * http://api.jquery.com/offset/
     */
    public offset(nativeEl:any):{width: number, height: number, top: number, left: number} {
        let boundingClientRect = nativeEl.getBoundingClientRect();
        return {
            width: boundingClientRect.width || nativeEl.offsetWidth,
            height: boundingClientRect.height || nativeEl.offsetHeight,
            top: boundingClientRect.top + (this.window.pageYOffset || this.document.documentElement.scrollTop),
            left: boundingClientRect.left + (this.window.pageXOffset || this.document.documentElement.scrollLeft)
        };
    }

    /**
     * Provides coordinates for the targetEl in relation to hostEl
     */
    public positionElements(hostEl:any, targetEl:any, positionStr:any, appendToBody:any):{top: number, left: number} {
        let positionStrParts = positionStr.split('-');
        let pos0 = positionStrParts[0];
        let pos1 = positionStrParts[1] || 'center';
        let hostElPos = appendToBody ?
            this.offset(hostEl) :
            this.position(hostEl);
        let targetElWidth = targetEl.offsetWidth;
        let targetElHeight = targetEl.offsetHeight;

        let shiftWidth:IAttribute = {
            center: function () {
                return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2;
            },
            left: function () {
                return hostElPos.left;
            },
            right: function () {
                return hostElPos.left + hostElPos.width;
            }
        };

        let shiftHeight:IAttribute = {
            center: function ():number {
                return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2;
            },
            top: function ():number {
                return hostElPos.top;
            },
            bottom: function ():number {
                return hostElPos.top + hostElPos.height;
            }
        };

        let targetElPos:{top: number, left: number};
        switch (pos0) {
            case 'right':
                targetElPos = {
                    top: shiftHeight[pos1](),
                    left: shiftWidth[pos0]()
                };
                break;
            case 'left':
                targetElPos = {
                    top: shiftHeight[pos1](),
                    left: hostElPos.left - targetElWidth
                };
                break;
            case 'bottom':
                targetElPos = {
                    top: shiftHeight[pos0](),
                    left: shiftWidth[pos1]()
                };
                break;
            default:
                targetElPos = {
                    top: hostElPos.top - targetElHeight,
                    left: shiftWidth[pos1]()
                };
                break;
        }

        return targetElPos;
    }
}

export const positionService = new PositionService();
import {
    Directive,
    OnInit, Input, HostListener,
    ElementRef, EventEmitter,
    DynamicComponentLoader, ComponentRef,
    Provider,
    Injectable, forwardRef, ResolvedBinding, Injector
} from 'angular2/core';
import {NgClass, NgStyle} from 'angular2/common';

import {TooltipOptions} from './tooltip-options.class';
import {TooltipContainer} from './tooltip-container.component';

import {IAttribute} from './IAttribute';

@Directive({selector: '[tooltip]'})
export class Tooltip implements OnInit {
    @Input('tooltip') public content:string;
    @Input('tooltipPlacement') public placement:string = 'top';
    @Input('tooltipIsOpen') public isOpen:boolean;
    @Input('tooltipEnable') public enable:boolean;
    @Input('tooltipAppendToBody') public appendToBody:boolean;

    private visible:boolean = false;
    private tooltip:Promise<ComponentRef>;

    constructor(public element:ElementRef,
                public loader:DynamicComponentLoader) {
    }

    ngOnInit() {
    }

    @HostListener('focusin', ['$event', '$target'])
    @HostListener('mouseenter', ['$event', '$target'])
    show() {
        if (this.visible) {
            return;
        }
        this.visible = true;

        let options = new TooltipOptions({
            content: this.content,
            placement: this.placement
        });

        let binding = Injector.resolve([
            new Provider(TooltipOptions, {useValue: options})
        ]);

        this.tooltip = this.loader
            .loadNextToLocation(TooltipContainer, this.element, binding)
            .then((componentRef:ComponentRef) => {
                componentRef.instance.position(this.element);
                return componentRef;
            });
    }

    @HostListener('focusout', ['$event', '$target'])
    @HostListener('mouseleave', ['$event', '$target'])
    hide() {
        if (!this.visible) {
            return;
        }
        this.visible = false;
        this.tooltip.then((componentRef:ComponentRef) => {
            componentRef.dispose();
            return componentRef;
        });
    }
}
import {
    Component,
    OnInit, Input, HostListener,
    ElementRef, EventEmitter,
    DynamicComponentLoader, ComponentRef, Inject, AfterViewChecked
} from 'angular2/core';
import {NgClass, NgStyle} from 'angular2/common';
import {positionService} from './position';
import {TooltipOptions} from './tooltip-options.class';

@Component({
    selector: 'tooltip-container',
    directives: [NgClass, NgStyle],
    template: `<div class="tooltip" role="tooltip"
     [ngStyle]="{top: top, left: left, display: display}"
     [ngClass]="classMap">
      <div class="tooltip-arrow"></div>
      <div class="tooltip-inner">
        {{content}}
      </div>
    </div>`
})
export class TooltipContainer implements AfterViewChecked {
    private classMap:any;
    private positionMap:any;
    private top:string;
    private left:string;
    private display:string;
    private content:string;
    private placement:string;
    private appendToBody:boolean;

    private isOpen:boolean;
    private hostEl:ElementRef;

    constructor(public element:ElementRef, @Inject(TooltipOptions) options:TooltipOptions) {
        Object.assign(this, options);
        this.classMap = {'in': false};
        this.classMap[options.placement] = true;
    }

    ngAfterViewChecked() {
        if (this.hostEl !== null) {
            let p = positionService
                .positionElements(this.hostEl.nativeElement,
                    this.element.nativeElement.children[0],
                    this.placement, this.appendToBody);
            this.top = p.top + 'px';
            this.left = p.left + 'px';
            this.classMap['in'] = true;
        }
    }

    public position(hostEl:ElementRef) {
        this.display = 'block';
        this.top = '-1000px';
        this.left = '-1000px';
        this.hostEl = hostEl;
    }
}
import {Injectable} from 'angular2/core';

@Injectable()
export class TooltipOptions {
    public placement:string;
    public popupClass:string;
    public animation:boolean;
    public isOpen:boolean;

    constructor(options:Object) {
        Object.assign(this, options);
    }
}
/**
 * Created by Tareq Boulakjar. from angulartypescript.com
 */
import {Component} from 'angular2/core';
import {CORE_DIRECTIVES, FORM_DIRECTIVES} from 'angular2/common';
import {Tooltip} from './tooltip.directive';
import {TooltipContainer} from './tooltip-container.component';

/*Angular 2 Tooltip Example*/
@Component({
    selector: 'my-app',
    template:`

                    <h4 style="color: #e85c45">Angular 2 Tooltip Example</h4>
                    <p>
                    Angular 2 rating <a href="#" tooltipPlacement="left" tooltip="stars (left)!">stars</a> will allow
                    the <a href="#" tooltipPlacement="right" tooltip="visitors (right)!">visitors</a>
                    of your website to <a href="#" tooltipPlacement="bottom" tooltip="rate (bottom)!">rate</a>
                    a <a href="#" tooltipPopupDelay='1000' tooltip='document (delay = 1000)'>document</a>
                    , <a href="#" tooltipPlacement="top" tooltip="image (top)!"> image </a> or something else by clicking on the stars.
                    </p>

                    <h4 style="color: #e86f4a">Angular 2 (Form) Tooltip Example</h4>
                    <form role="form">
                      <div class="form-group">
                        <label>Tooltip on <b>Input</b>: </label>
                        <input type="text" value="Show the tooltip !"
                         tooltip="Please enter a value : ) (i'm a tooltip)"
                         tooltipPlacement="bottom"
                         class="form-control" />
                      </div>
                    </form>

             `,
    directives: [Tooltip, TooltipContainer, CORE_DIRECTIVES, FORM_DIRECTIVES],
    styles: [`
                /* Specify styling for tooltip contents */
                .tooltip.customClass .tooltip-inner {
                    color: #880000;
                    background-color: #ffff66;
                    box-shadow: 0 6px 12px rgba(0,0,0,.175);
                }
                /* Hide arrow */
                .tooltip.customClass .tooltip-arrow {
                    display: none;
                }
             `]
})
export class Angular2Tooltip {

}