interface GlobuleStatic {
    /**
     * Match one or more globbing patterns against one or more file paths.
     * Returns a uniqued array of all file paths that match any of the specified globbing patterns.
     */
    match(patterns: string | string[], filepaths: string | string[], options?: minimatch.IOptions): string[];

    /**
     * Tests pattern(s) against against one or more file paths and returns true if any files were matched, otherwise false.
     */
    isMatch(patterns: string | string[], filepaths: string | string[], options?: minimatch.IOptions): boolean;

    /**
     * Returns a unique array of all file or directory paths that match the given globbing pattern(s)
     */
    find(pattern: string | string[], options?: FindOptions): string[];

    /**
     * Returns a unique array of all file or directory paths that match the given globbing pattern(s)
     */
    find(options: FindOptions): string[];

    /**
     * Returns a unique array of all file or directory paths that match the given globbing pattern(s)
     */
    find(pattern: string | string[], pattern2: string | string[], options?: FindOptions): string[];

    /**
     * Returns a unique array of all file or directory paths that match the given globbing pattern(s)
     */
    find(pattern: string, pattern2: string, pattern3: string | string[], options?: FindOptions): string[];

    /**
     * Given a set of source file paths, returns an array of src-dest file mapping objects
     */
    mapping(filepaths: string[], options?: MappingOptions): OneMapping[];

    /**
     * Given a set of source file paths, returns an array of src-dest file mapping objects
     */
    mapping(options: MappingOptions): OneMapping[];

    /**
     * Given a set of source file paths, returns an array of src-dest file mapping objects
     */
    mapping(filepaths: string[], filepaths2: string[], options?: MappingOptions): OneMapping[];

    /**
     * Given a set of source file paths, returns an array of src-dest file mapping objects
     */
    mapping(filepaths: string[], filepaths2: string[], filepaths3: string[], options?: MappingOptions): OneMapping[];
}


item => +item


@Component({
  selector: 'nz-badge-sup',
  exportAs: 'nzBadgeSup',
  preserveWhitespaces: false,
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [zoomBadgeMotion],
  template: `
    <ng-container *ngIf="count <= nzOverflowCount; else overflowTemplate">
      <span
        [nzNoAnimation]="noAnimation"
        *ngFor="let n of maxNumberArray; let i = index"
        class="ant-scroll-number-only"
        [style.transform]="'translateY(' + -countArray[i] * 100 + '%)'"
      >
        <ng-container *ngIf="!nzDot && countArray[i] !== undefined">
          <p *ngFor="let p of countSingleArray" class="ant-scroll-number-only-unit" [class.current]="p === countArray[i]">
            {{ p }}
          </p>
        </ng-container>
      </span>
    </ng-container>
    <ng-template #overflowTemplate>{{ nzOverflowCount }}+</ng-template>
  `,
  host: {
    '[@.disabled]': `disableAnimation`,
    '[@zoomBadgeMotion]': '',
    '[attr.title]': `nzTitle === null ? '' : nzTitle || nzCount`,
    '[style]': `nzStyle`,
    '[style.right.px]': `nzOffset && nzOffset[0] ? -nzOffset[0] : null`,
    '[style.margin-top.px]': `nzOffset && nzOffset[1] ? nzOffset[1] : null`,
    '[class.ant-badge-count]': `!nzDot`,
    '[class.ant-badge-dot]': `nzDot`,
    '[class.ant-badge-multiple-words]': `countArray.length >= 2`
  }
})
export class NzBadgeSupComponent implements OnInit, OnChanges {
  @Input() nzOffset?: [number, number];
  @Input() nzTitle?: string | null | undefined;
  @Input() nzStyle: { [key: string]: string } | null = null;
  @Input() nzDot = false;
  @Input() nzOverflowCount: number = 99;
  @Input() disableAnimation = false;
  @Input() nzCount?: number | TemplateRef<NzSafeAny>;
  @Input() noAnimation = false;
  maxNumberArray: string[] = [];
  countArray: number[] = [];
  count: number = 0;
  countSingleArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  constructor(private elementRef: ElementRef) {
    // TODO: move to host after View Engine deprecation
    this.elementRef.nativeElement.classList.add('ant-scroll-number');
  }

  generateMaxNumberArray(): void {
    this.maxNumberArray = this.nzOverflowCount.toString().split('');
  }

  ngOnInit(): void {
    this.generateMaxNumberArray();
  }

  ngOnChanges(changes: SimpleChanges): void {
    const { nzOverflowCount, nzCount } = changes;
    if (nzCount && typeof nzCount.currentValue === 'number') {
      this.count = Math.max(0, nzCount.currentValue);
      this.countArray = this.count
        .toString()
        .split('')
        .map(item => +item);
    }
    if (nzOverflowCount) {
      this.generateMaxNumberArray();
    }
  }
}


generateMaxNumberArray(): void {
    this.maxNumberArray = this.nzOverflowCount.toString().split('');
  }


ngOnInit(): void {
    this.generateMaxNumberArray();
  }


ngOnChanges(changes: SimpleChanges): void {
    const { nzOverflowCount, nzCount } = changes;
    if (nzCount && typeof nzCount.currentValue === 'number') {
      this.count = Math.max(0, nzCount.currentValue);
      this.countArray = this.count
        .toString()
        .split('')
        .map(item => +item);
    }
    if (nzOverflowCount) {
      this.generateMaxNumberArray();
    }
  }


export function slotGlowEffect(): Script {
    const sprite = EntityMemory.to<number>(0)
    const center = EntityMemory.to<number>(1)
    const width = EntityMemory.to<number>(2)

    const startTime = EntityMemory.to<number>(3)
    const endTime = EntityMemory.to<number>(4)

    const tL = EntityMemory.to<number>(5)
    const tR = EntityMemory.to<number>(6)
    const bL = EntityMemory.to<number>(7)
    const bR = EntityMemory.to<number>(8)
    const z = EntityMemory.to<number>(9)

    const tw = Multiply(lane.w, Lerp(1, 1.25, options.slotEffectSize))

    const initialize = [
        startTime.set(Time),
        endTime.set(Add(startTime, 0.25)),

        tL.set(Multiply(Subtract(center, width), tw)),
        tR.set(Multiply(Add(center, width), tw)),
        bL.set(Multiply(Subtract(center, width), lane.w)),
        bR.set(Multiply(Add(center, width), lane.w)),
        z.set(
            Add(
                Layer.SlotGlowEffect,
                Divide(Time, 1000),
                Divide(Abs(center), -10000)
            )
        ),
    ]

    const a = EntityMemory.to<number>(32)
    const p = EntityMemory.to<number>(33)
    const t = EntityMemory.to<number>(34)

    const updateParallel = Or(GreaterOr(Time, endTime), [
        a.set(Unlerp(endTime, startTime, Time)),
        p.set(Subtract(1, Power(a, 3))),
        t.set(Add(lane.b, Multiply(lane.w, 4, options.slotEffectSize, p))),
        Draw(
            sprite,
            bL,
            lane.b,
            Lerp(bL, tL, p),
            t,
            Lerp(bR, tR, p),
            t,
            bR,
            lane.b,
            z,
            a
        ),
    ])

    return {
        initialize: {
            code: initialize,
        },
        updateParallel: {
            code: updateParallel,
        },
    }
}


export function getSlotGlowSprite(color: number) {
    return customSkinSprite(engineId, 60 + color)
}


@NgModule({
    declarations: [
        FormGroupComponent,
        FormFieldComponent,
        InputMessageGroupWithTemplate
    ],
    imports: [
        CommonModule,
        FormsModule,
        ReactiveFormsModule,
        FdFormModule,
        InlineHelpModule,
        PopoverModule
    ],
    exports: [
        FormGroupComponent,
        FormFieldComponent
    ]
})
export class FdpFormGroupModule {
}


/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// This file must match with:
//  * Layouts Protocol.h
export class Protocol {

	static Name_User_Persistent_Panel  = "user-persistent-panel";

    static Name_Action_Update_Item     = 'update-item';
    static Name_Action_Update_Children = 'update-children';
    static Name_Action_Create_Item     = 'create-item';

	static Type_Dummy                  = "dummy";
	static Type_Panel                  = "panel";
	static Type_Connexion              = "connexion";

	static Name_Connexion_Name         = "name";
	static Name_Connexion_Host         = "host";
	static Name_Connexion_Port         = "port";

}


function getWindow(element) { return element.querySelector('ngb-popover-window'); }


(html: string) =>
    createGenericTestComponent(html, TestComponent) as ComponentFixture<TestComponent>


(html: string) => <ComponentFixture<TestOnPushComponent>>createGenericTestComponent(html, TestOnPushComponent)


() => {
  beforeEach(() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbPopoverModule]}); });

  afterEach(() => {
    // Cleaning elements, because of a TestBed issue with the id attribute
    Array.from(document.body.children).map((element: HTMLElement) => {
      if (element.tagName.toLocaleLowerCase() === 'div') {
        element.parentNode.removeChild(element);
      }
    });
  });

  it('should render popover on top by default', () => {
    const fixture = TestBed.createComponent(NgbPopoverWindow);
    fixture.componentInstance.title = 'Test title';
    fixture.detectChanges();

    expect(fixture.nativeElement).toHaveCssClass('popover');
    expect(fixture.nativeElement).not.toHaveCssClass('bs-popover-top');
    expect(fixture.nativeElement.getAttribute('role')).toBe('tooltip');
    expect(fixture.nativeElement.querySelector('.popover-header').textContent).toBe('Test title');
  });

  it('should optionally have a custom class', () => {
    const fixture = TestBed.createComponent(NgbPopoverWindow);
    fixture.detectChanges();

    expect(fixture.nativeElement).not.toHaveCssClass('my-custom-class');

    fixture.componentInstance.popoverClass = 'my-custom-class';
    fixture.detectChanges();

    expect(fixture.nativeElement).toHaveCssClass('my-custom-class');
  });
}


() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbPopoverModule]}); }


() => {
    // Cleaning elements, because of a TestBed issue with the id attribute
    Array.from(document.body.children).map((element: HTMLElement) => {
      if (element.tagName.toLocaleLowerCase() === 'div') {
        element.parentNode.removeChild(element);
      }
    });
  }


(element: HTMLElement) => {
      if (element.tagName.toLocaleLowerCase() === 'div') {
        element.parentNode.removeChild(element);
      }
    }


() => {
    const fixture = TestBed.createComponent(NgbPopoverWindow);
    fixture.componentInstance.title = 'Test title';
    fixture.detectChanges();

    expect(fixture.nativeElement).toHaveCssClass('popover');
    expect(fixture.nativeElement).not.toHaveCssClass('bs-popover-top');
    expect(fixture.nativeElement.getAttribute('role')).toBe('tooltip');
    expect(fixture.nativeElement.querySelector('.popover-header').textContent).toBe('Test title');
  }


() => {
    const fixture = TestBed.createComponent(NgbPopoverWindow);
    fixture.detectChanges();

    expect(fixture.nativeElement).not.toHaveCssClass('my-custom-class');

    fixture.componentInstance.popoverClass = 'my-custom-class';
    fixture.detectChanges();

    expect(fixture.nativeElement).toHaveCssClass('my-custom-class');
  }


() => {
    TestBed.configureTestingModule({
      declarations: [TestComponent, TestOnPushComponent, DestroyableCmpt, TestHooksComponent],
      imports: [NgbPopoverModule],
      providers: [SpyService]
    });
  }


() => {
      const fixture =
          createTestComponent(`<div ngbPopover="Great tip!" popoverTitle="Title" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      const id = windowEl.getAttribute('id');

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl.textContent.trim()).toBe('TitleGreat tip!');
      expect(windowEl.getAttribute('role')).toBe('tooltip');
      expect(windowEl.parentNode).toBe(fixture.nativeElement);
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBe(id);

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
    }


() => {
      const fixture = createTestComponent(`
          <ng-template #t>Hello, {{name}}!</ng-template>
          <div [ngbPopover]="t" popoverTitle="Title" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      const id = windowEl.getAttribute('id');

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl.textContent.trim()).toBe('TitleHello, World!');
      expect(windowEl.getAttribute('role')).toBe('tooltip');
      expect(windowEl.parentNode).toBe(fixture.nativeElement);
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBe(id);

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
    }


() => {
      const fixture = createTestComponent(`
          <ng-template #t let-name="name">Hello, {{name}}!</ng-template>
          <div [ngbPopover]="t" popoverTitle="Title" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      directive.context.popover.open({name: 'John'});
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      const id = windowEl.getAttribute('id');

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl.textContent.trim()).toBe('TitleHello, John!');
      expect(windowEl.getAttribute('role')).toBe('tooltip');
      expect(windowEl.parentNode).toBe(fixture.nativeElement);
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBe(id);

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
    }


() => {
      const fixture = createTestComponent(`

`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      const id = windowEl.getAttribute('id');

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl).toHaveCssClass('my-custom-class');
      expect(windowEl.textContent.trim()).toBe('TitleGreat tip!');
      expect(windowEl.getAttribute('role')).toBe('tooltip');
      expect(windowEl.parentNode).toBe(fixture.nativeElement);
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBe(id);

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(directive.nativeElement.getAttribute('aria-describedby')).toBeNull();
    }


() => {
      const fixture =
          createTestComponent(`<div ngbPopover="Great tip!" popoverTitle="Title" [popoverClass]="popoverClass"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      expect(windowEl).not.toHaveCssClass('my-popover-class');

      fixture.componentInstance.popoverClass = 'my-popover-class';
      fixture.detectChanges();
      expect(windowEl).toHaveCssClass('my-popover-class');
    }


() => {
      const fixture = createTestComponent(`
          <ng-template #t>Hello, {{name}}! <destroyable-cmpt></destroyable-cmpt></ng-template>

`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));
      const spyService = fixture.debugElement.injector.get(SpyService);

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      expect(windowEl.textContent.trim()).toBe('Hello, World! Some contentBody');
      expect(spyService.called).toBeFalsy();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(spyService.called).toBeTruthy();
    }


() => {
      const fixture = createTestComponent(`
          <ng-template #t let-greeting="greeting">{{greeting}}, {{name}}!</ng-template>

`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      fixture.componentInstance.name = 'tout le monde';
      fixture.componentInstance.popover.open({greeting: 'Bonjour'});
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      expect(windowEl.textContent.trim()).toBe('Bonjour, tout le monde!!!');

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`
          <ng-template #t><destroyable-cmpt></destroyable-cmpt></ng-template>
          <div [ngbPopover]="t" popoverTitle="Title"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));
      const spyService = fixture.debugElement.injector.get(SpyService);

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
      expect(spyService.called).toBeFalsy();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(spyService.called).toBeTruthy();
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);
      expect(windowEl.querySelector('.popover-header')).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div [ngbPopover]="" [popoverTitle]=""></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div [ngbPopover]="Disabled!" [disablePopover]="true"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div [ngbPopover]="name" [popoverTitle]="title"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      fixture.componentInstance.name = '';
      fixture.componentInstance.title = '';
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div [ngbPopover]="" popoverTitle="title"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).not.toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div [ngbPopover]="name" popoverTitle="title"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      fixture.componentInstance.name = '';
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" popoverTitle="Title"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
    }


() => {
      const fixture = createTestComponent(
          `<ng-template [ngIf]="show"><div ngbPopover="Great tip!" popoverTitle="Title"></div></ng-template>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      fixture.componentInstance.show = false;
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<ng-template [ngIf]="show">

                                        </ng-template>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      fixture.componentInstance.show = false;
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = TestBed.createComponent(TestHooksComponent);
      fixture.detectChanges();

      const popoverWindow = fixture.debugElement.query(By.directive(NgbPopoverWindow));
      expect(popoverWindow.nativeElement).toHaveCssClass('popover');
    }


() => {

    it('should use requested position', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" placement="left"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    });

    it('should properly position popovers when a component is using the OnPush strategy', () => {
      const fixture = createOnPushTestComponent(`<div ngbPopover="Great tip!" placement="left"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    });

    it('should have proper arrow placement', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" placement="right-top"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-right');
      expect(windowEl).toHaveCssClass('bs-popover-right-top');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    });

    it('should accept placement in array (second value of the array should be applied)', () => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" [placement]="['left-top','top-left']" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl).toHaveCssClass('bs-popover-top-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    });

    it('should accept placement with space separated values (second value should be applied)', () => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" placement="left-top top-left" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl).toHaveCssClass('bs-popover-top-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    });

    it('should apply auto placement', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" placement="auto"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      // actual placement with auto is not known in advance, so use regex to check it
      expect(windowEl.getAttribute('class')).toMatch('bs-popover-\.');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    });

  }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" placement="left"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    }


() => {
      const fixture = createOnPushTestComponent(`<div ngbPopover="Great tip!" placement="left"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" placement="right-top"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-right');
      expect(windowEl).toHaveCssClass('bs-popover-right-top');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    }


() => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" [placement]="['left-top','top-left']" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl).toHaveCssClass('bs-popover-top-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    }


() => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" placement="left-top top-left" style="margin-top: 100px;"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      expect(windowEl).toHaveCssClass('bs-popover-top');
      expect(windowEl).toHaveCssClass('bs-popover-top-left');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" placement="auto"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      const windowEl = getWindow(fixture.nativeElement);

      expect(windowEl).toHaveCssClass('popover');
      // actual placement with auto is not known in advance, so use regex to check it
      expect(windowEl.getAttribute('class')).toMatch('bs-popover-\.');
      expect(windowEl.textContent.trim()).toBe('Great tip!');
    }


() => {

    it('should be appended to the element matching the selector passed to "container"', () => {
      const selector = 'body';
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" container="` + selector + `"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(getWindow(window.document.querySelector(selector))).not.toBeNull();
    });

    it('should properly destroy popovers when the "container" option is used', () => {
      const selector = 'body';
      const fixture =
          createTestComponent(`<div *ngIf="show" ngbPopover="Great tip!" container="` + selector + `"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();

      expect(getWindow(document.querySelector(selector))).not.toBeNull();
      fixture.componentRef.instance.show = false;
      fixture.detectChanges();
      expect(getWindow(document.querySelector(selector))).toBeNull();
    });

  }


() => {
      const selector = 'body';
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" container="` + selector + `"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(getWindow(window.document.querySelector(selector))).not.toBeNull();
    }


() => {
      const selector = 'body';
      const fixture =
          createTestComponent(`<div *ngIf="show" ngbPopover="Great tip!" container="` + selector + `"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();

      expect(getWindow(document.querySelector(selector))).not.toBeNull();
      fixture.componentRef.instance.show = false;
      fixture.detectChanges();
      expect(getWindow(document.querySelector(selector))).toBeNull();
    }


() => {
    it('should emit events when showing and hiding popover', () => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" triggers="click" (shown)="shown()" (hidden)="hidden()"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      let shownSpy = spyOn(fixture.componentInstance, 'shown');
      let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
      expect(shownSpy).toHaveBeenCalled();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(hiddenSpy).toHaveBeenCalled();
    });

    it('should not emit close event when already closed', () => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" triggers="manual" (shown)="shown()" (hidden)="hidden()"></div>`);

      let shownSpy = spyOn(fixture.componentInstance, 'shown');
      let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');

      fixture.componentInstance.popover.open();
      fixture.detectChanges();

      fixture.componentInstance.popover.open();
      fixture.detectChanges();

      expect(getWindow(fixture.nativeElement)).not.toBeNull();
      expect(shownSpy).toHaveBeenCalled();
      expect(shownSpy.calls.count()).toEqual(1);
      expect(hiddenSpy).not.toHaveBeenCalled();
    });

    it('should not emit open event when already opened', () => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" triggers="manual" (shown)="shown()" (hidden)="hidden()"></div>`);

      let shownSpy = spyOn(fixture.componentInstance, 'shown');
      let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');

      fixture.componentInstance.popover.close();
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(shownSpy).not.toHaveBeenCalled();
      expect(hiddenSpy).not.toHaveBeenCalled();
    });

    it('should report correct visibility', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="manual"></div>`);
      fixture.detectChanges();

      expect(fixture.componentInstance.popover.isOpen()).toBeFalsy();

      fixture.componentInstance.popover.open();
      fixture.detectChanges();
      expect(fixture.componentInstance.popover.isOpen()).toBeTruthy();

      fixture.componentInstance.popover.close();
      fixture.detectChanges();
      expect(fixture.componentInstance.popover.isOpen()).toBeFalsy();
    });
  }


() => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" triggers="click" (shown)="shown()" (hidden)="hidden()"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      let shownSpy = spyOn(fixture.componentInstance, 'shown');
      let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
      expect(shownSpy).toHaveBeenCalled();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(hiddenSpy).toHaveBeenCalled();
    }


() => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" triggers="manual" (shown)="shown()" (hidden)="hidden()"></div>`);

      let shownSpy = spyOn(fixture.componentInstance, 'shown');
      let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');

      fixture.componentInstance.popover.open();
      fixture.detectChanges();

      fixture.componentInstance.popover.open();
      fixture.detectChanges();

      expect(getWindow(fixture.nativeElement)).not.toBeNull();
      expect(shownSpy).toHaveBeenCalled();
      expect(shownSpy.calls.count()).toEqual(1);
      expect(hiddenSpy).not.toHaveBeenCalled();
    }


() => {
      const fixture = createTestComponent(
          `<div ngbPopover="Great tip!" triggers="manual" (shown)="shown()" (hidden)="hidden()"></div>`);

      let shownSpy = spyOn(fixture.componentInstance, 'shown');
      let hiddenSpy = spyOn(fixture.componentInstance, 'hidden');

      fixture.componentInstance.popover.close();
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
      expect(shownSpy).not.toHaveBeenCalled();
      expect(hiddenSpy).not.toHaveBeenCalled();
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="manual"></div>`);
      fixture.detectChanges();

      expect(fixture.componentInstance.popover.isOpen()).toBeFalsy();

      fixture.componentInstance.popover.open();
      fixture.detectChanges();
      expect(fixture.componentInstance.popover.isOpen()).toBeTruthy();

      fixture.componentInstance.popover.close();
      fixture.detectChanges();
      expect(fixture.componentInstance.popover.isOpen()).toBeFalsy();
    }


() => {
    beforeEach(() => { TestBed.configureTestingModule({declarations: [TestComponent], imports: [NgbPopoverModule]}); });

    it('should support toggle triggers', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="click"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });

    it('should non-default toggle triggers', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="mouseenter:click"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });

    it('should support multiple triggers', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="mouseenter:mouseleave click"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });

    it('should not use default for manual triggers', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="manual"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });

    it('should allow toggling for manual triggers', () => {
      const fixture = createTestComponent(`

                <button (click)="t.toggle()">T</button>`);
      const button = fixture.nativeElement.querySelector('button');

      triggerEvent(button, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(button, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });

    it('should allow open / close for manual triggers', () => {
      const fixture = createTestComponent(`

                <button (click)="t.open()">O</button>
                <button (click)="t.close()">C</button>`);
      const buttons = fixture.nativeElement.querySelectorAll('button');

      triggerEvent(buttons[0], 'click');  // open
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(buttons[1], 'click');  // close
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });

    it('should not throw when open called for manual triggers and open popover', () => {
      const fixture = createTestComponent(`

                <button (click)="t.open()">O</button>`);
      const button = fixture.nativeElement.querySelector('button');

      triggerEvent(button, 'click');  // open
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(button, 'click');  // open
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
    });

    it('should not throw when closed called for manual triggers and closed popover', () => {
      const fixture = createTestComponent(`

                <button (click)="t.close()">C</button>`);
      const button = fixture.nativeElement.querySelector('button');

      triggerEvent(button, 'click');  // close
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    });
  }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="click"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="mouseenter:click"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="mouseenter:mouseleave click"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(directive, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!" triggers="manual"></div>`);
      const directive = fixture.debugElement.query(By.directive(NgbPopover));

      triggerEvent(directive, 'mouseenter');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`

                <button (click)="t.toggle()">T</button>`);
      const button = fixture.nativeElement.querySelector('button');

      triggerEvent(button, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(button, 'click');
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`

                <button (click)="t.open()">O</button>
                <button (click)="t.close()">C</button>`);
      const buttons = fixture.nativeElement.querySelectorAll('button');

      triggerEvent(buttons[0], 'click');  // open
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(buttons[1], 'click');  // close
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
      const fixture = createTestComponent(`

                <button (click)="t.open()">O</button>`);
      const button = fixture.nativeElement.querySelector('button');

      triggerEvent(button, 'click');  // open
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();

      triggerEvent(button, 'click');  // open
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).not.toBeNull();
    }


() => {
      const fixture = createTestComponent(`

                <button (click)="t.close()">C</button>`);
      const button = fixture.nativeElement.querySelector('button');

      triggerEvent(button, 'click');  // close
      fixture.detectChanges();
      expect(getWindow(fixture.nativeElement)).toBeNull();
    }


() => {
    let config: NgbPopoverConfig;

    beforeEach(() => {
      TestBed.configureTestingModule({imports: [NgbPopoverModule]});
      TestBed.overrideComponent(TestComponent, {set: {template: `<div ngbPopover="Great tip!"></div>`}});
    });

    beforeEach(inject([NgbPopoverConfig], (c: NgbPopoverConfig) => {
      config = c;
      config.placement = 'bottom';
      config.triggers = 'hover';
      config.container = 'body';
      config.popoverClass = 'my-custom-class';
    }));

    it('should initialize inputs with provided config', () => {
      const fixture = TestBed.createComponent(TestComponent);
      fixture.detectChanges();

      const popover = fixture.componentInstance.popover;

      expect(popover.placement).toBe(config.placement);
      expect(popover.triggers).toBe(config.triggers);
      expect(popover.container).toBe(config.container);
      expect(popover.popoverClass).toBe(config.popoverClass);
    });
  }


() => {
      TestBed.configureTestingModule({imports: [NgbPopoverModule]});
      TestBed.overrideComponent(TestComponent, {set: {template: `<div ngbPopover="Great tip!"></div>`}});
    }


(c: NgbPopoverConfig) => {
      config = c;
      config.placement = 'bottom';
      config.triggers = 'hover';
      config.container = 'body';
      config.popoverClass = 'my-custom-class';
    }


() => {
      const fixture = TestBed.createComponent(TestComponent);
      fixture.detectChanges();

      const popover = fixture.componentInstance.popover;

      expect(popover.placement).toBe(config.placement);
      expect(popover.triggers).toBe(config.triggers);
      expect(popover.container).toBe(config.container);
      expect(popover.popoverClass).toBe(config.popoverClass);
    }


() => {
    let config = new NgbPopoverConfig();
    config.placement = 'bottom';
    config.triggers = 'hover';
    config.popoverClass = 'my-custom-class';

    beforeEach(() => {
      TestBed.configureTestingModule(
          {imports: [NgbPopoverModule], providers: [{provide: NgbPopoverConfig, useValue: config}]});
    });

    it('should initialize inputs with provided config as provider', () => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!"></div>`);
      const popover = fixture.componentInstance.popover;

      expect(popover.placement).toBe(config.placement);
      expect(popover.triggers).toBe(config.triggers);
      expect(popover.popoverClass).toBe(config.popoverClass);
    });
  }


() => {
      TestBed.configureTestingModule(
          {imports: [NgbPopoverModule], providers: [{provide: NgbPopoverConfig, useValue: config}]});
    }


() => {
      const fixture = createTestComponent(`<div ngbPopover="Great tip!"></div>`);
      const popover = fixture.componentInstance.popover;

      expect(popover.placement).toBe(config.placement);
      expect(popover.triggers).toBe(config.triggers);
      expect(popover.popoverClass).toBe(config.popoverClass);
    }


() => {

    /**
     * Under very specific conditions ngOnDestroy can be invoked without calling ngOnInit first.
     * See discussion in https://github.com/ng-bootstrap/ng-bootstrap/issues/2199 for more details.
     */
    it('should not try to call listener cleanup function when no listeners registered', () => {
      const fixture = createTestComponent(`
         <ng-template #tpl>

</ng-template>
         <button (click)="createAndDestroyTplWithAPopover(tpl)"></button>
       `);
      const buttonEl = fixture.debugElement.query(By.css('button'));
      triggerEvent(buttonEl, 'click');
    });
  }


() => {
      const fixture = createTestComponent(`
         <ng-template #tpl>

</ng-template>
         <button (click)="createAndDestroyTplWithAPopover(tpl)"></button>
       `);
      const buttonEl = fixture.debugElement.query(By.css('button'));
      triggerEvent(buttonEl, 'click');
    }


@Injectable()
class SpyService {
  called = false;
}


@Component({selector: 'test-cmpt', template: ``})
export class TestComponent {
  name = 'World';
  show = true;
  title: string;
  placement: string;
  popoverClass: string;

  @ViewChild(NgbPopover, {static: true}) popover: NgbPopover;

  constructor(private _vcRef: ViewContainerRef) {}

  createAndDestroyTplWithAPopover(tpl: TemplateRef<any>) {
    this._vcRef.createEmbeddedView(tpl, {}, 0);
    this._vcRef.remove(0);
  }

  shown() {}
  hidden() {}
}


@Component({selector: 'test-onpush-cmpt', changeDetection: ChangeDetectionStrategy.OnPush, template: ``})
export class TestOnPushComponent {
}


@Component({selector: 'destroyable-cmpt', template: 'Some content'})
export class DestroyableCmpt implements OnDestroy {
  constructor(private _spyService: SpyService) {}

  ngOnDestroy(): void { this._spyService.called = true; }
}


@Component({selector: 'test-hooks', template: `<div ngbPopover="popover"></div>`})
export class TestHooksComponent implements AfterViewInit {
  @ViewChild(NgbPopover, {static: true}) popover;

  ngAfterViewInit() { this.popover.open(); }
}


createAndDestroyTplWithAPopover(tpl: TemplateRef<any>) {
    this._vcRef.createEmbeddedView(tpl, {}, 0);
    this._vcRef.remove(0);
  }


shown() {}


hidden() {}


ngOnDestroy(): void { this._spyService.called = true; }


ngAfterViewInit() { this.popover.open(); }


/**
 * Creates and initializes a basic panel state.
 */
export function createPanelState<
  TEmbeddableInput extends EmbeddableInput,
  TPlacementMethodArgs extends IPanelPlacementArgs = IPanelPlacementArgs
>(
  panelState: PanelState<TEmbeddableInput>,
  currentPanels: { [key: string]: DashboardPanelState },
  placementMethod?: PanelPlacementMethod<TPlacementMethodArgs>,
  placementArgs?: TPlacementMethodArgs
): DashboardPanelState<TEmbeddableInput> {
  const defaultPlacementArgs = {
    width: DEFAULT_PANEL_WIDTH,
    height: DEFAULT_PANEL_HEIGHT,
    currentPanels,
  };
  const finalPlacementArgs = placementArgs
    ? {
        ...defaultPlacementArgs,
        ...placementArgs,
      }
    : defaultPlacementArgs;

  const gridDataLocation = placementMethod
    ? placementMethod(finalPlacementArgs as TPlacementMethodArgs)
    : findTopLeftMostOpenSpace(defaultPlacementArgs);

  return {
    gridData: {
      ...gridDataLocation,
      i: panelState.explicitInput.id,
    },
    ...panelState,
  };
}


export default class BlogPost extends Component<any, any> {
  static contextTypes = {
    auth: PropTypes.instanceOf(AuthService).isRequired
  };

  constructor() {
    super();
    this.state = {
      post: null,
      comments: []
    };
  }

  componentWillMount() {
    this.fetchBlogPost();
  }

  render() {
    const auth: AuthService = this.context.auth;
    const { post, comments } = this.state;
    const user = auth.getProfile();

    log.info(`Rendering post`, post);

    return (
      <div>
        <BlogPostDetail post={post}/>
        <CommentThread comments={comments} user={user}
                       onCommented={(content: string) => this.submitComment(user, post, content)}


componentWillMount() {
    this.fetchBlogPost();
  }


render() {
    const auth: AuthService = this.context.auth;
    const { post, comments } = this.state;
    const user = auth.getProfile();

    log.info(`Rendering post`, post);

    return (
      <div>
        <BlogPostDetail post={post}/>
        <CommentThread comments={comments} user={user}
                       onCommented={(content: string) => this.submitComment(user, post, content)}


(props: Props, ref: React.RefObject<HTMLSpanElement>) => (
    createIcon(props, ref, {
        filled: <path fillRule="evenodd" clipRule="evenodd" d="M10.1388 15.6904L7.27676 12.6904C7.23376 12.6454 7.21276 12.5894 7.17976 12.5394C7.14376 12.4854 7.10076 12.4374 7.07576 12.3774C7.02676 12.2564 6.99976 12.1284 6.99976 12.0004C6.99976 11.8694 7.02676 11.7394 7.07676 11.6174C7.12776 11.4954 7.20076 11.3844 7.29376 11.2924L10.2928 8.29343C10.6838 7.90243 11.3168 7.90243 11.7068 8.29343C12.0978 8.68343 12.0978 9.31643 11.7068 9.70743L10.4138 11.0004H15.9998C16.5528 11.0004 16.9998 11.4474 16.9998 12.0004C16.9998 12.5524 16.5528 13.0004 15.9998 13.0004H10.3368L11.5858 14.3094C11.9668 14.7094 11.9528 15.3414 11.5528 15.7234C11.1528 16.1054 10.5198 16.0894 10.1388 15.6904ZM21.9998 12.0004C21.9998 6.48543 17.5138 2.00043 11.9998 2.00043C6.48576 2.00043 1.99976 6.48543 1.99976 12.0004C1.99976 17.5144 6.48576 22.0004 11.9998 22.0004C17.5138 22.0004 21.9998 17.5144 21.9998 12.0004Z"/>,
        outline: <path fillRule="evenodd" clipRule="evenodd" d="M16 11H10.336L11.586 9.69C11.967 9.291 11.952 8.658 11.553 8.276C11.153 7.896 10.521 7.91 10.139 8.31L7.276 11.31C7.238 11.35 7.221 11.4 7.19 11.445C7.149 11.504 7.103 11.558 7.076 11.626C7.039 11.717 7.027 11.812 7.019 11.909C7.016 11.94 7 11.968 7 12C7 12.002 7.001 12.003 7.001 12.005C7.002 12.135 7.027 12.264 7.078 12.386C7.128 12.507 7.202 12.616 7.293 12.708L10.293 15.707C10.488 15.902 10.744 16 11 16C11.256 16 11.512 15.902 11.707 15.707C12.098 15.316 12.098 14.684 11.707 14.293L10.414 13H16C16.552 13 17 12.552 17 12C17 11.448 16.552 11 16 11ZM12 20C7.589 20 4 16.411 4 12C4 7.589 7.589 4 12 4C16.411 4 20 7.589 20 12C20 16.411 16.411 20 12 20ZM12 2C6.486 2 2 6.486 2 12C2 17.514 6.486 22 12 22C17.514 22 22 17.514 22 12C22 6.486 17.514 2 12 2Z"/>,
    })
)


({ qmsProcessRoute }) => {
            this.qmsProcessRoute = qmsProcessRoute;
            this.makeTime = this.qmsProcessRoute.makeTime != null ? this.qmsProcessRoute.makeTime.format(DATE_TIME_FORMAT) : null;
            this.modifyTime = this.qmsProcessRoute.modifyTime != null ? this.qmsProcessRoute.modifyTime.format(DATE_TIME_FORMAT) : null;
        }


(res: HttpResponse<IQmsProcessRoute>) => this.onSaveSuccess()


(res: HttpErrorResponse) => this.onSaveError()


@Component({
    selector: 'jhi-qms-process-route-update',
    templateUrl: './qms-process-route-update.component.html'
})
export class QmsProcessRouteUpdateComponent implements OnInit {
    qmsProcessRoute: IQmsProcessRoute;
    isSaving: boolean;
    makeTime: string;
    modifyTime: string;

    constructor(private qmsProcessRouteService: QmsProcessRouteService, private activatedRoute: ActivatedRoute) {}

    ngOnInit() {
        this.isSaving = false;
        this.activatedRoute.data.subscribe(({ qmsProcessRoute }) => {
            this.qmsProcessRoute = qmsProcessRoute;
            this.makeTime = this.qmsProcessRoute.makeTime != null ? this.qmsProcessRoute.makeTime.format(DATE_TIME_FORMAT) : null;
            this.modifyTime = this.qmsProcessRoute.modifyTime != null ? this.qmsProcessRoute.modifyTime.format(DATE_TIME_FORMAT) : null;
        });
    }

    previousState() {
        window.history.back();
    }

    save() {
        this.isSaving = true;
        this.qmsProcessRoute.makeTime = this.makeTime != null ? moment(this.makeTime, DATE_TIME_FORMAT) : null;
        this.qmsProcessRoute.modifyTime = this.modifyTime != null ? moment(this.modifyTime, DATE_TIME_FORMAT) : null;
        if (this.qmsProcessRoute.id !== undefined) {
            this.subscribeToSaveResponse(this.qmsProcessRouteService.update(this.qmsProcessRoute));
        } else {
            this.subscribeToSaveResponse(this.qmsProcessRouteService.create(this.qmsProcessRoute));
        }
    }

    private subscribeToSaveResponse(result: Observable<HttpResponse<IQmsProcessRoute>>) {
        result.subscribe((res: HttpResponse<IQmsProcessRoute>) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError());
    }

    private onSaveSuccess() {
        this.isSaving = false;
        this.previousState();
    }

    private onSaveError() {
        this.isSaving = false;
    }
}


ngOnInit() {
        this.isSaving = false;
        this.activatedRoute.data.subscribe(({ qmsProcessRoute }) => {
            this.qmsProcessRoute = qmsProcessRoute;
            this.makeTime = this.qmsProcessRoute.makeTime != null ? this.qmsProcessRoute.makeTime.format(DATE_TIME_FORMAT) : null;
            this.modifyTime = this.qmsProcessRoute.modifyTime != null ? this.qmsProcessRoute.modifyTime.format(DATE_TIME_FORMAT) : null;
        });
    }


save() {
        this.isSaving = true;
        this.qmsProcessRoute.makeTime = this.makeTime != null ? moment(this.makeTime, DATE_TIME_FORMAT) : null;
        this.qmsProcessRoute.modifyTime = this.modifyTime != null ? moment(this.modifyTime, DATE_TIME_FORMAT) : null;
        if (this.qmsProcessRoute.id !== undefined) {
            this.subscribeToSaveResponse(this.qmsProcessRouteService.update(this.qmsProcessRoute));
        } else {
            this.subscribeToSaveResponse(this.qmsProcessRouteService.create(this.qmsProcessRoute));
        }
    }


private subscribeToSaveResponse(result: Observable<HttpResponse<IQmsProcessRoute>>) {
        result.subscribe((res: HttpResponse<IQmsProcessRoute>) => this.onSaveSuccess(), (res: HttpErrorResponse) => this.onSaveError());
    }


private onSaveSuccess() {
        this.isSaving = false;
        this.previousState();
    }


private onSaveError() {
        this.isSaving = false;
    }


export function HTMLDetailsElementGenerator(HTMLElement: Constructable<IHTMLElement>) {
  return class HTMLDetailsElement extends HTMLElement implements IHTMLDetailsElement, PromiseLike<IHTMLDetailsElement> {
    constructor() {
      super();
      setState(this, {
        createInstanceName: 'createHTMLDetailsElement',
      });
    }

    // properties

    public get open(): Promise<boolean> {
      return awaitedHandler.getProperty<boolean>(this, 'open', false);
    }

    public then<TResult1 = IHTMLDetailsElement, TResult2 = never>(onfulfilled?: ((value: IHTMLDetailsElement) => (PromiseLike<TResult1> | TResult1)) | undefined | null, onrejected?: ((reason: any) => (PromiseLike<TResult2> | TResult2)) | undefined | null): Promise<TResult1 | TResult2> {
      return nodeFactory.createInstanceWithNodePointer(this).then(onfulfilled, onrejected);
    }

    public [Symbol.for('nodejs.util.inspect.custom')]() {
      return inspectInstanceProperties(this, HTMLDetailsElementPropertyKeys, HTMLDetailsElementConstantKeys);
    }
  };
}


// INTERFACES RELATED TO STATE MACHINE PROPERTIES ////////////////////////////
export interface IHTMLDetailsElementProperties extends IHTMLElementProperties {
  awaitedPath: AwaitedPath;
  awaitedOptions: any;
  createInstanceName: string;

  readonly open?: Promise<boolean>;
}


public then<TResult1 = IHTMLDetailsElement, TResult2 = never>(onfulfilled?: ((value: IHTMLDetailsElement) => (PromiseLike<TResult1> | TResult1)) | undefined | null, onrejected?: ((reason: any) => (PromiseLike<TResult2> | TResult2)) | undefined | null): Promise<TResult1 | TResult2> {
      return nodeFactory.createInstanceWithNodePointer(this).then(onfulfilled, onrejected);
    }


public [Symbol.for('nodejs.util.inspect.custom')]() {
      return inspectInstanceProperties(this, HTMLDetailsElementPropertyKeys, HTMLDetailsElementConstantKeys);
    }


export function castTensor<T extends Tensor>(
    x: T, dtype: DataType, backend: KernelBackend): T {
  if (!hasEncodingLoss(x.dtype, dtype)) {
    // We don't change the underlying data, since we cast to higher
    // precision.
    return Tensor.make(x.shape, {dataId: x.dataId}, dtype) as T;
  }
  if (dtype === 'int32') {
    return backend.int(x);
  } else if (dtype === 'bool') {
    return backend.notEqual(x, TensorOps.scalar(0, x.dtype)) as T;
  } else {
    throw new Error(`Error in Cast: unknown dtype argument (${dtype})`);
  }
}


export function reshapeTensor<T extends Tensor<Rank>, R extends Rank>(
    x: T, shape: ShapeMap[R]): Tensor<R> {
  return Tensor.make(shape, {dataId: x.dataId}, x.dtype);
}


p => p instanceof Visibility


(p) => p instanceof Attribute


(p) => p instanceof Query


(dep) => {
      if (isPresent(dep.attributeName)) {
        ListWrapper.push(readAttributes, dep.attributeName);
      }
    }


eventObj => view.triggerEventHandlers(this.eventName, eventObj, boundElementIndex)


actionObj => view.callAction(boundElementIndex, this.actionExpression, actionObj)


eventName => new EventEmitterAccessor(
                                                 eventName, reflector.getter(eventName))


(actionExpression, actionName) => {ListWrapper.push(
            res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))}


b => {ListWrapper.push(bd, new BindingData(b, LIGHT_DOM))}


b => {
      ListWrapper.forEach(b.resolvedHostInjectables, b => {
        ListWrapper.push(bd, new BindingData(ProtoElementInjector._createBinding(b), LIGHT_DOM));
      });
    }


b => {
        ListWrapper.push(bd, new BindingData(ProtoElementInjector._createBinding(b), LIGHT_DOM));
      }


b => ListWrapper.push(bd,
                              new BindingData(ProtoElementInjector._createBinding(b), SHADOW_DOM))


d => DependencyWithVisibility.createFrom(d)


(q) => q.update()


class StaticKeys {
  viewManagerId: number;
  protoViewId: number;
  viewContainerId: number;
  changeDetectorRefId: number;
  elementRefId: number;

  constructor() {
    // TODO: vsavkin Key.annotate(Key.get(AppView), 'static')
    this.viewManagerId = Key.get(avmModule.AppViewManager).id;
    this.protoViewId = Key.get(ProtoViewRef).id;
    this.viewContainerId = Key.get(ViewContainerRef).id;
    this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
    this.elementRefId = Key.get(ElementRef).id;
  }

  static instance(): StaticKeys {
    if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
    return _staticKeys;
  }
}


export class TreeNode<T extends TreeNode<any>> {
  _parent: T;
  _head: T = null;
  _tail: T = null;
  _next: T = null;
  constructor(parent: T) {
    if (isPresent(parent)) parent.addChild(this);
  }

  _assertConsistency(): void {
    this._assertHeadBeforeTail();
    this._assertTailReachable();
    this._assertPresentInParentList();
  }

  _assertHeadBeforeTail(): void {
    if (isBlank(this._tail) && isPresent(this._head))
      throw new BaseException('null tail but non-null head');
  }

  _assertTailReachable(): void {
    if (isBlank(this._tail)) return;
    if (isPresent(this._tail._next)) throw new BaseException('node after tail');
    var p = this._head;
    while (isPresent(p) && p != this._tail) p = p._next;
    if (isBlank(p) && isPresent(this._tail)) throw new BaseException('tail not reachable.')
  }

  _assertPresentInParentList(): void {
    var p = this._parent;
    if (isBlank(p)) {
      return;
    }
    var cur = p._head;
    while (isPresent(cur) && cur != this) cur = cur._next;
    if (isBlank(cur)) throw new BaseException('node not reachable through parent.')
  }

  /**
   * Adds a child to the parent node. The child MUST NOT be a part of a tree.
   */
  addChild(child: T): void {
    if (isPresent(this._tail)) {
      this._tail._next = child;
      this._tail = child;
    } else {
      this._tail = this._head = child;
    }
    child._next = null;
    child._parent = this;
    this._assertConsistency();
  }

  /**
   * Adds a child to the parent node after a given sibling.
   * The child MUST NOT be a part of a tree and the sibling must be present.
   */
  addChildAfter(child: T, prevSibling: T): void {
    this._assertConsistency();
    if (isBlank(prevSibling)) {
      var prevHead = this._head;
      this._head = child;
      child._next = prevHead;
      if (isBlank(this._tail)) this._tail = child;
    } else if (isBlank(prevSibling._next)) {
      this.addChild(child);
      return;
    } else {
      prevSibling._assertPresentInParentList();
      child._next = prevSibling._next;
      prevSibling._next = child;
    }
    child._parent = this;
    this._assertConsistency();
  }

  /**
   * Detaches a node from the parent's tree.
   */
  remove(): void {
    this._assertConsistency();
    if (isBlank(this.parent)) return;
    var nextSibling = this._next;
    var prevSibling = this._findPrev();
    if (isBlank(prevSibling)) {
      this.parent._head = this._next;
    } else {
      prevSibling._next = this._next;
    }
    if (isBlank(nextSibling)) {
      this._parent._tail = prevSibling;
    }
    this._parent._assertConsistency();
    this._parent = null;
    this._next = null;
    this._assertConsistency();
  }

  /**
   * Finds a previous sibling or returns null if first child.
   * Assumes the node has a parent.
   * TODO(rado): replace with DoublyLinkedList to avoid O(n) here.
   */
  _findPrev() {
    var node = this.parent._head;
    if (node == this) return null;
    while (node._next !== this) node = node._next;
    return node;
  }

  get parent() { return this._parent; }

  // TODO(rado): replace with a function call, does too much work for a getter.
  get children() {
    var res = [];
    var child = this._head;
    while (child != null) {
      ListWrapper.push(res, child);
      child = child._next;
    }
    return res;
  }
}


export class DependencyWithVisibility extends Dependency {
  constructor(key: Key, asPromise: boolean, lazy: boolean, optional: boolean, properties: List<any>,
              public visibility: Visibility) {
    super(key, asPromise, lazy, optional, properties);
  }

  static createFrom(d: Dependency): Dependency {
    return new DependencyWithVisibility(d.key, d.asPromise, d.lazy, d.optional, d.properties,
                                        DependencyWithVisibility._visibility(d.properties));
  }

  static _visibility(properties): Visibility {
    if (properties.length == 0) return self;
    var p = ListWrapper.find(properties, p => p instanceof Visibility);
    return isPresent(p) ? p : self;
  }
}


export class DirectiveDependency extends DependencyWithVisibility {
  constructor(key: Key, asPromise: boolean, lazy: boolean, optional: boolean, properties: List<any>,
              visibility: Visibility, public attributeName: string, public queryDirective) {
    super(key, asPromise, lazy, optional, properties, visibility);
    this._verify();
  }

  _verify(): void {
    var count = 0;
    if (isPresent(this.queryDirective)) count++;
    if (isPresent(this.attributeName)) count++;
    if (count > 1)
      throw new BaseException(
          'A directive injectable can contain only one of the following @Attribute or @Query.');
  }

  static createFrom(d: Dependency): Dependency {
    return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties,
                                   DependencyWithVisibility._visibility(d.properties),
                                   DirectiveDependency._attributeName(d.properties),
                                   DirectiveDependency._query(d.properties));
  }

  static _attributeName(properties): string {
    var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
    return isPresent(p) ? p.attributeName : null;
  }

  static _query(properties) {
    var p = ListWrapper.find(properties, (p) => p instanceof Query);
    return isPresent(p) ? resolveForwardRef(p.directive) : null;
  }
}


export class DirectiveBinding extends ResolvedBinding {
  constructor(key: Key, factory: Function, dependencies: List<Dependency>,
              providedAsPromise: boolean, public resolvedAppInjectables: List<ResolvedBinding>,
              public resolvedHostInjectables: List<ResolvedBinding>,
              public resolvedViewInjectables: List<ResolvedBinding>,
              public metadata: DirectiveMetadata) {
    super(key, factory, dependencies, providedAsPromise);
  }

  get callOnDestroy(): boolean { return this.metadata.callOnDestroy; }

  get callOnChange(): boolean { return this.metadata.callOnChange; }

  get callOnAllChangesDone(): boolean { return this.metadata.callOnAllChangesDone; }

  get displayName(): string { return this.key.displayName; }

  get eventEmitters(): List<string> {
    return isPresent(this.metadata) && isPresent(this.metadata.events) ? this.metadata.events : [];
  }

  get hostActions(): Map<string, string> {
    return isPresent(this.metadata) && isPresent(this.metadata.hostActions) ?
               this.metadata.hostActions :
               MapWrapper.create();
  }

  get changeDetection() { return this.metadata.changeDetection; }

  static createFromBinding(binding: Binding, ann: Directive): DirectiveBinding {
    if (isBlank(ann)) {
      ann = new Directive();
    }

    var rb = binding.resolve();
    var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
    var resolvedAppInjectables = ann instanceof Component && isPresent(ann.appInjector) ?
                                                    Injector.resolve(ann.appInjector) :
                                                    [];
    var resolvedHostInjectables =
        isPresent(ann.hostInjector) ? resolveBindings(ann.hostInjector) : [];
    var resolvedViewInjectables = ann instanceof Component && isPresent(ann.viewInjector) ?
                                                     resolveBindings(ann.viewInjector) :
                                                     [];
    var metadata = new DirectiveMetadata({
      id: stringify(rb.key.token),
      type: ann instanceof
          Component ? DirectiveMetadata.COMPONENT_TYPE : DirectiveMetadata.DIRECTIVE_TYPE,
      selector: ann.selector,
      compileChildren: ann.compileChildren,
      events: ann.events,
      hostListeners:
          isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
      hostProperties:
          isPresent(ann.hostProperties) ? MapWrapper.createFromStringMap(ann.hostProperties) : null,
      hostAttributes:
          isPresent(ann.hostAttributes) ? MapWrapper.createFromStringMap(ann.hostAttributes) : null,
      hostActions: isPresent(ann.hostActions) ? MapWrapper.createFromStringMap(ann.hostActions) :
                                                null,
      properties: ann.properties,
      readAttributes: DirectiveBinding._readAttributes(deps),

      callOnDestroy: hasLifecycleHook(onDestroy, rb.key.token, ann),
      callOnChange: hasLifecycleHook(onChange, rb.key.token, ann),
      callOnCheck: hasLifecycleHook(onCheck, rb.key.token, ann),
      callOnInit: hasLifecycleHook(onInit, rb.key.token, ann),
      callOnAllChangesDone: hasLifecycleHook(onAllChangesDone, rb.key.token, ann),

      changeDetection: ann instanceof
          Component ? ann.changeDetection : null
    });
    return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise,
                                resolvedAppInjectables, resolvedHostInjectables,
                                resolvedViewInjectables, metadata);
  }

  static _readAttributes(deps) {
    var readAttributes = [];
    ListWrapper.forEach(deps, (dep) => {
      if (isPresent(dep.attributeName)) {
        ListWrapper.push(readAttributes, dep.attributeName);
      }
    });
    return readAttributes;
  }

  static createFromType(type: Type, annotation: Directive): DirectiveBinding {
    var binding = new Binding(type, {toClass: type});
    return DirectiveBinding.createFromBinding(binding, annotation);
  }
}


// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
export class PreBuiltObjects {
  constructor(public viewManager: avmModule.AppViewManager, public view: viewModule.AppView,
              public protoView: viewModule.AppProtoView) {}
}


export class EventEmitterAccessor {
  constructor(public eventName: string, public getter: Function) {}

  subscribe(view: viewModule.AppView, boundElementIndex: number, directive: Object) {
    var eventEmitter = this.getter(directive);
    return ObservableWrapper.subscribe(
        eventEmitter,
        eventObj => view.triggerEventHandlers(this.eventName, eventObj, boundElementIndex));
  }
}


export class HostActionAccessor {
  constructor(public actionExpression: string, public getter: Function) {}

  subscribe(view: viewModule.AppView, boundElementIndex: number, directive: Object) {
    var eventEmitter = this.getter(directive);
    return ObservableWrapper.subscribe(
        eventEmitter,
        actionObj => view.callAction(boundElementIndex, this.actionExpression, actionObj));
  }
}


export class BindingData {
  constructor(public binding: ResolvedBinding, public visibility: number) {}

  getKeyId() { return this.binding.key.id; }

  createEventEmitterAccessors() {
    if (!(this.binding instanceof DirectiveBinding)) return [];
    var db = <DirectiveBinding>this.binding;
    return ListWrapper.map(db.eventEmitters, eventName => new EventEmitterAccessor(
                                                 eventName, reflector.getter(eventName)));
  }

  createHostActionAccessors() {
    if (!(this.binding instanceof DirectiveBinding)) return [];
    var res = [];
    var db = <DirectiveBinding>this.binding;
    MapWrapper.forEach(
        db.hostActions,
        (actionExpression, actionName) => {ListWrapper.push(
            res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))});
    return res;
  }
}


/**

Difference between di.Injector and ElementInjector

di.Injector:
 - imperative based (can create child injectors imperativly)
 - Lazy loading of code
 - Component/App Level services which are usually not DOM Related.

ElementInjector:
  - ProtoBased (Injector structure fixed at compile time)
  - understands @Ancestor, @Parent, @Child, @Descendent
  - Fast
  - Query mechanism for children
  - 1:1 to DOM structure.

 PERF BENCHMARK:
http://www.williambrownstreet.net/blog/2014/04/faster-angularjs-rendering-angularjs-and-reactjs/
 */
export class ProtoElementInjector {
  view: viewModule.AppView;
  attributes: Map<string, string>;
  eventEmitterAccessors: List<List<EventEmitterAccessor>>;
  hostActionAccessors: List<List<HostActionAccessor>>;

  /** Whether the element is exported as $implicit. */
  exportElement: boolean;

  /** Whether the component instance is exported as $implicit. */
  exportComponent: boolean;

  /** The variable name that will be set to $implicit for the element. */
  exportImplicitName: string;

  _strategy: _ProtoElementInjectorStrategy;

  static create(parent: ProtoElementInjector, index: number, bindings: List<ResolvedBinding>,
                firstBindingIsComponent: boolean, distanceToParent: number) {
    var bd = [];

    ProtoElementInjector._createDirectiveBindingData(bindings, bd, firstBindingIsComponent);
    ProtoElementInjector._createHostInjectorBindingData(bindings, bd);
    if (firstBindingIsComponent) {
      ProtoElementInjector._createViewInjectorBindingData(bindings, bd);
    }

    return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent);
  }

  private static _createDirectiveBindingData(bindings: List<ResolvedBinding>, bd: List<BindingData>,
                                             firstBindingIsComponent: boolean) {
    if (firstBindingIsComponent) {
      ListWrapper.push(bd, new BindingData(bindings[0], LIGHT_DOM_AND_SHADOW_DOM));
      for (var i = 1; i < bindings.length; ++i) {
        ListWrapper.push(bd, new BindingData(bindings[i], LIGHT_DOM));
      }
    } else {
      ListWrapper.forEach(bindings, b => {ListWrapper.push(bd, new BindingData(b, LIGHT_DOM))});
    }
  }

  private static _createHostInjectorBindingData(bindings: List<ResolvedBinding>,
                                                bd: List<BindingData>) {
    ListWrapper.forEach(bindings, b => {
      ListWrapper.forEach(b.resolvedHostInjectables, b => {
        ListWrapper.push(bd, new BindingData(ProtoElementInjector._createBinding(b), LIGHT_DOM));
      });
    });
  }

  private static _createViewInjectorBindingData(bindings: List<ResolvedBinding>,
                                                bd: List<BindingData>) {
    var db = <DirectiveBinding>bindings[0];
    ListWrapper.forEach(
        db.resolvedViewInjectables,
        b => ListWrapper.push(bd,
                              new BindingData(ProtoElementInjector._createBinding(b), SHADOW_DOM)));
  }

  private static _createBinding(b: ResolvedBinding) {
    var deps = ListWrapper.map(b.dependencies, d => DependencyWithVisibility.createFrom(d));
    return new ResolvedBinding(b.key, b.factory, deps, b.providedAsPromise);
  }

  constructor(public parent: ProtoElementInjector, public index: int, bd: List<BindingData>,
              public distanceToParent: number, public _firstBindingIsComponent: boolean) {
    this.exportComponent = false;
    this.exportElement = false;

    var length = bd.length;
    this.eventEmitterAccessors = ListWrapper.createFixedSize(length);
    this.hostActionAccessors = ListWrapper.createFixedSize(length);

    this._strategy = length > _MAX_DIRECTIVE_CONSTRUCTION_COUNTER ?
                         new _ProtoElementInjectorDynamicStrategy(this, bd) :
                         new _ProtoElementInjectorInlineStrategy(this, bd);
  }

  instantiate(parent: ElementInjector): ElementInjector {
    return new ElementInjector(this, parent);
  }

  directParent(): ProtoElementInjector { return this.distanceToParent < 2 ? this.parent : null; }

  get hasBindings(): boolean { return this._strategy.hasBindings(); }

  getBindingAtIndex(index: number): any { return this._strategy.getBindingAtIndex(index); }
}


/**
 * Strategy used by the `ProtoElementInjector` when the number of bindings is 10 or less.
 * In such a case, inlining fields is benefitial for performances.
 */
class _ProtoElementInjectorInlineStrategy implements _ProtoElementInjectorStrategy {
  // only _binding0 can contain a component
  _binding0: ResolvedBinding = null;
  _binding1: ResolvedBinding = null;
  _binding2: ResolvedBinding = null;
  _binding3: ResolvedBinding = null;
  _binding4: ResolvedBinding = null;
  _binding5: ResolvedBinding = null;
  _binding6: ResolvedBinding = null;
  _binding7: ResolvedBinding = null;
  _binding8: ResolvedBinding = null;
  _binding9: ResolvedBinding = null;

  _keyId0: number = null;
  _keyId1: number = null;
  _keyId2: number = null;
  _keyId3: number = null;
  _keyId4: number = null;
  _keyId5: number = null;
  _keyId6: number = null;
  _keyId7: number = null;
  _keyId8: number = null;
  _keyId9: number = null;

  _visibility0: number = null;
  _visibility1: number = null;
  _visibility2: number = null;
  _visibility3: number = null;
  _visibility4: number = null;
  _visibility5: number = null;
  _visibility6: number = null;
  _visibility7: number = null;
  _visibility8: number = null;
  _visibility9: number = null;

  constructor(protoEI: ProtoElementInjector, bd: List<BindingData>) {
    var length = bd.length;

    if (length > 0) {
      this._binding0 = bd[0].binding;
      this._keyId0 = bd[0].getKeyId();
      this._visibility0 = bd[0].visibility;
      protoEI.eventEmitterAccessors[0] = bd[0].createEventEmitterAccessors();
      protoEI.hostActionAccessors[0] = bd[0].createHostActionAccessors();
    }
    if (length > 1) {
      this._binding1 = bd[1].binding;
      this._keyId1 = bd[1].getKeyId();
      this._visibility1 = bd[1].visibility;
      protoEI.eventEmitterAccessors[1] = bd[1].createEventEmitterAccessors();
      protoEI.hostActionAccessors[1] = bd[1].createHostActionAccessors();
    }
    if (length > 2) {
      this._binding2 = bd[2].binding;
      this._keyId2 = bd[2].getKeyId();
      this._visibility2 = bd[2].visibility;
      protoEI.eventEmitterAccessors[2] = bd[2].createEventEmitterAccessors();
      protoEI.hostActionAccessors[2] = bd[2].createHostActionAccessors();
    }
    if (length > 3) {
      this._binding3 = bd[3].binding;
      this._keyId3 = bd[3].getKeyId();
      this._visibility3 = bd[3].visibility;
      protoEI.eventEmitterAccessors[3] = bd[3].createEventEmitterAccessors();
      protoEI.hostActionAccessors[3] = bd[3].createHostActionAccessors();
    }
    if (length > 4) {
      this._binding4 = bd[4].binding;
      this._keyId4 = bd[4].getKeyId();
      this._visibility4 = bd[4].visibility;
      protoEI.eventEmitterAccessors[4] = bd[4].createEventEmitterAccessors();
      protoEI.hostActionAccessors[4] = bd[4].createHostActionAccessors();
    }
    if (length > 5) {
      this._binding5 = bd[5].binding;
      this._keyId5 = bd[5].getKeyId();
      this._visibility5 = bd[5].visibility;
      protoEI.eventEmitterAccessors[5] = bd[5].createEventEmitterAccessors();
      protoEI.hostActionAccessors[5] = bd[5].createHostActionAccessors();
    }
    if (length > 6) {
      this._binding6 = bd[6].binding;
      this._keyId6 = bd[6].getKeyId();
      this._visibility6 = bd[6].visibility;
      protoEI.eventEmitterAccessors[6] = bd[6].createEventEmitterAccessors();
      protoEI.hostActionAccessors[6] = bd[6].createHostActionAccessors();
    }
    if (length > 7) {
      this._binding7 = bd[7].binding;
      this._keyId7 = bd[7].getKeyId();
      this._visibility7 = bd[7].visibility;
      protoEI.eventEmitterAccessors[7] = bd[7].createEventEmitterAccessors();
      protoEI.hostActionAccessors[7] = bd[7].createHostActionAccessors();
    }
    if (length > 8) {
      this._binding8 = bd[8].binding;
      this._keyId8 = bd[8].getKeyId();
      this._visibility8 = bd[8].visibility;
      protoEI.eventEmitterAccessors[8] = bd[8].createEventEmitterAccessors();
      protoEI.hostActionAccessors[8] = bd[8].createHostActionAccessors();
    }
    if (length > 9) {
      this._binding9 = bd[9].binding;
      this._keyId9 = bd[9].getKeyId();
      this._visibility9 = bd[9].visibility;
      protoEI.eventEmitterAccessors[9] = bd[9].createEventEmitterAccessors();
      protoEI.hostActionAccessors[9] = bd[9].createHostActionAccessors();
    }
  }

  hasBindings(): boolean { return isPresent(this._binding0); }

  getBindingAtIndex(index: number): any {
    if (index == 0) return this._binding0;
    if (index == 1) return this._binding1;
    if (index == 2) return this._binding2;
    if (index == 3) return this._binding3;
    if (index == 4) return this._binding4;
    if (index == 5) return this._binding5;
    if (index == 6) return this._binding6;
    if (index == 7) return this._binding7;
    if (index == 8) return this._binding8;
    if (index == 9) return this._binding9;
    throw new OutOfBoundsAccess(index);
  }

  createElementInjectorStrategy(ei: ElementInjector): _ElementInjectorStrategy {
    return new ElementInjectorInlineStrategy(this, ei);
  }
}


/**
 * Strategy used by the `ProtoElementInjector` when the number of bindings is more than 10.
 */
class _ProtoElementInjectorDynamicStrategy implements _ProtoElementInjectorStrategy {
  // only _bindings[0] can contain a component
  _bindings: List<ResolvedBinding>;
  _keyIds: List<number>;
  _visibilities: List<number>;

  constructor(protoInj: ProtoElementInjector, bd: List<BindingData>) {
    var len = bd.length;

    this._bindings = ListWrapper.createFixedSize(len);
    this._keyIds = ListWrapper.createFixedSize(len);
    this._visibilities = ListWrapper.createFixedSize(len);

    for (var i = 0; i < len; i++) {
      this._bindings[i] = bd[i].binding;
      this._keyIds[i] = bd[i].getKeyId();
      this._visibilities[i] = bd[i].visibility;
      protoInj.eventEmitterAccessors[i] = bd[i].createEventEmitterAccessors();
      protoInj.hostActionAccessors[i] = bd[i].createHostActionAccessors();
    }
  }

  hasBindings(): boolean { return isPresent(this._bindings[0]); }

  getBindingAtIndex(index: number): any {
    if (index < 0 || index >= this._bindings.length) {
      throw new OutOfBoundsAccess(index);
    }

    return this._bindings[index];
  }

  createElementInjectorStrategy(ei: ElementInjector) {
    return new ElementInjectorDynamicStrategy(this, ei);
  }
}


/**
 * Strategy used by the `ElementInjector` when the number of bindings is 10 or less.
 * In such a case, inlining fields is benefitial for performances.
 */
class ElementInjectorDynamicStrategy implements _ElementInjectorStrategy {
  // If this element injector has a component, the component instance will be stored in _objs[0]
  _objs: List<any>;

  constructor(public _protoStrategy: _ProtoElementInjectorDynamicStrategy,
              public _ei: ElementInjector) {
    this._objs = ListWrapper.createFixedSize(_protoStrategy._bindings.length);
  }

  callOnDestroy(): void {
    var p = this._protoStrategy;

    for (var i = 0; i < p._bindings.length; i++) {
      if (p._bindings[i] instanceof DirectiveBinding &&
                                        (<DirectiveBinding>p._bindings[i]).callOnDestroy) {
        this._objs[i].onDestroy();
      }
    }
  }

  clearInstances(): void { ListWrapper.fill(this._objs, null); }

  hydrate(): void {
    var p = this._protoStrategy;

    for (var i = 0; i < p._keyIds.length; i++) {
      if (isPresent(p._keyIds[i])) {
        this.getObjByKeyId(p._keyIds[i], LIGHT_DOM_AND_SHADOW_DOM);
      }
    }
  }

  getComponent(): any { return this._objs[0]; }

  isComponentKey(key: Key): boolean {
    return this._ei._proto._firstBindingIsComponent && isPresent(key) &&
           key.id === this._protoStrategy._keyIds[0];
  }

  buildQueries(): void {
    var p = this._protoStrategy;

    for (var i = 0; i < p._bindings.length; i++) {
      if (p._bindings[i] instanceof DirectiveBinding) {
        this._ei._buildQueriesForDeps(<List<DirectiveDependency>>p._bindings[i].dependencies);
      }
    }
  }

  getObjByKeyId(keyId: number, visibility: number): any {
    var p = this._protoStrategy;

    // TODO(vicb): optimize lookup ?
    for (var i = 0; i < p._keyIds.length; i++) {
      if (p._keyIds[i] === keyId && (p._visibilities[i] & visibility) > 0) {
        if (isBlank(this._objs[i])) {
          this._objs[i] = this._ei._new(p._bindings[i]);
        }

        return this._objs[i];
      }
    }

    return _undefined;
  }

  getDirectiveAtIndex(index: number): any {
    if (index < 0 || index >= this._objs.length) {
      throw new OutOfBoundsAccess(index);
    }

    return this._objs[index];
  }

  getComponentBinding(): DirectiveBinding {
    return <DirectiveBinding>this._protoStrategy._bindings[0];
  }

  getMaxDirectives(): number { return this._objs.length; }
}


class OutOfBoundsAccess extends BaseException {
  message: string;
  constructor(index) {
    super();
    this.message = `Index ${index} is out-of-bounds.`;
  }

  toString(): string { return this.message; }
}


class QueryError extends BaseException {
  message: string;
  // TODO(rado): pass the names of the active directives.
  constructor() {
    super();
    this.message = 'Only 3 queries can be concurrently active in a template.';
  }

  toString(): string { return this.message; }
}


class QueryRef {
  directive;
  list: QueryList<any>;
  originator: ElementInjector;

  constructor(directive, list: QueryList<any>, originator: ElementInjector) {
    this.directive = directive;
    this.list = list;
    this.originator = originator;
  }

  update(): void {
    var aggregator = [];
    this.visit(this.originator, aggregator);
    this.list.reset(aggregator);
  }

  visit(inj: ElementInjector, aggregator): void {
    if (isBlank(inj)) return;
    if (inj.hasDirective(this.directive)) {
      ListWrapper.push(aggregator, inj.get(this.directive));
    }
    var child = inj._head;
    while (isPresent(child)) {
      this.visit(child, aggregator);
      child = child._next;
    }
  }
}


interface _ProtoElementInjectorStrategy {
  hasBindings(): boolean;
  getBindingAtIndex(index: number): any;
  createElementInjectorStrategy(ei: ElementInjector): _ElementInjectorStrategy;
}


interface _ElementInjectorStrategy {
  callOnDestroy(): void;
  clearInstances(): void;
  hydrate(): void;
  getComponent(): any;
  isComponentKey(key: Key): boolean;
  buildQueries(): void;
  getObjByKeyId(keyId: number, visibility: number): any;
  getDirectiveAtIndex(index: number): any;
  getComponentBinding(): DirectiveBinding;
  getMaxDirectives(): number;
}


_assertConsistency(): void {
    this._assertHeadBeforeTail();
    this._assertTailReachable();
    this._assertPresentInParentList();
  }


_assertHeadBeforeTail(): void {
    if (isBlank(this._tail) && isPresent(this._head))
      throw new BaseException('null tail but non-null head');
  }


_assertTailReachable(): void {
    if (isBlank(this._tail)) return;
    if (isPresent(this._tail._next)) throw new BaseException('node after tail');
    var p = this._head;
    while (isPresent(p) && p != this._tail) p = p._next;
    if (isBlank(p) && isPresent(this._tail)) throw new BaseException('tail not reachable.')
  }


_assertPresentInParentList(): void {
    var p = this._parent;
    if (isBlank(p)) {
      return;
    }
    var cur = p._head;
    while (isPresent(cur) && cur != this) cur = cur._next;
    if (isBlank(cur)) throw new BaseException('node not reachable through parent.')
  }


/**
   * Adds a child to the parent node. The child MUST NOT be a part of a tree.
   */
addChild(child: T): void {
    if (isPresent(this._tail)) {
      this._tail._next = child;
      this._tail = child;
    } else {
      this._tail = this._head = child;
    }
    child._next = null;
    child._parent = this;
    this._assertConsistency();
  }


/**
   * Adds a child to the parent node after a given sibling.
   * The child MUST NOT be a part of a tree and the sibling must be present.
   */
addChildAfter(child: T, prevSibling: T): void {
    this._assertConsistency();
    if (isBlank(prevSibling)) {
      var prevHead = this._head;
      this._head = child;
      child._next = prevHead;
      if (isBlank(this._tail)) this._tail = child;
    } else if (isBlank(prevSibling._next)) {
      this.addChild(child);
      return;
    } else {
      prevSibling._assertPresentInParentList();
      child._next = prevSibling._next;
      prevSibling._next = child;
    }
    child._parent = this;
    this._assertConsistency();
  }


/**
   * Detaches a node from the parent's tree.
   */
remove(): void {
    this._assertConsistency();
    if (isBlank(this.parent)) return;
    var nextSibling = this._next;
    var prevSibling = this._findPrev();
    if (isBlank(prevSibling)) {
      this.parent._head = this._next;
    } else {
      prevSibling._next = this._next;
    }
    if (isBlank(nextSibling)) {
      this._parent._tail = prevSibling;
    }
    this._parent._assertConsistency();
    this._parent = null;
    this._next = null;
    this._assertConsistency();
  }


/**
   * Finds a previous sibling or returns null if first child.
   * Assumes the node has a parent.
   * TODO(rado): replace with DoublyLinkedList to avoid O(n) here.
   */
_findPrev() {
    var node = this.parent._head;
    if (node == this) return null;
    while (node._next !== this) node = node._next;
    return node;
  }


static createFrom(d: Dependency): Dependency {
    return new DependencyWithVisibility(d.key, d.asPromise, d.lazy, d.optional, d.properties,
                                        DependencyWithVisibility._visibility(d.properties));
  }


static _visibility(properties): Visibility {
    if (properties.length == 0) return self;
    var p = ListWrapper.find(properties, p => p instanceof Visibility);
    return isPresent(p) ? p : self;
  }


_verify(): void {
    var count = 0;
    if (isPresent(this.queryDirective)) count++;
    if (isPresent(this.attributeName)) count++;
    if (count > 1)
      throw new BaseException(
          'A directive injectable can contain only one of the following @Attribute or @Query.');
  }


static createFrom(d: Dependency): Dependency {
    return new DirectiveDependency(d.key, d.asPromise, d.lazy, d.optional, d.properties,
                                   DependencyWithVisibility._visibility(d.properties),
                                   DirectiveDependency._attributeName(d.properties),
                                   DirectiveDependency._query(d.properties));
  }


static _attributeName(properties): string {
    var p = ListWrapper.find(properties, (p) => p instanceof Attribute);
    return isPresent(p) ? p.attributeName : null;
  }


static _query(properties) {
    var p = ListWrapper.find(properties, (p) => p instanceof Query);
    return isPresent(p) ? resolveForwardRef(p.directive) : null;
  }


static createFromBinding(binding: Binding, ann: Directive): DirectiveBinding {
    if (isBlank(ann)) {
      ann = new Directive();
    }

    var rb = binding.resolve();
    var deps = ListWrapper.map(rb.dependencies, DirectiveDependency.createFrom);
    var resolvedAppInjectables = ann instanceof Component && isPresent(ann.appInjector) ?
                                                    Injector.resolve(ann.appInjector) :
                                                    [];
    var resolvedHostInjectables =
        isPresent(ann.hostInjector) ? resolveBindings(ann.hostInjector) : [];
    var resolvedViewInjectables = ann instanceof Component && isPresent(ann.viewInjector) ?
                                                     resolveBindings(ann.viewInjector) :
                                                     [];
    var metadata = new DirectiveMetadata({
      id: stringify(rb.key.token),
      type: ann instanceof
          Component ? DirectiveMetadata.COMPONENT_TYPE : DirectiveMetadata.DIRECTIVE_TYPE,
      selector: ann.selector,
      compileChildren: ann.compileChildren,
      events: ann.events,
      hostListeners:
          isPresent(ann.hostListeners) ? MapWrapper.createFromStringMap(ann.hostListeners) : null,
      hostProperties:
          isPresent(ann.hostProperties) ? MapWrapper.createFromStringMap(ann.hostProperties) : null,
      hostAttributes:
          isPresent(ann.hostAttributes) ? MapWrapper.createFromStringMap(ann.hostAttributes) : null,
      hostActions: isPresent(ann.hostActions) ? MapWrapper.createFromStringMap(ann.hostActions) :
                                                null,
      properties: ann.properties,
      readAttributes: DirectiveBinding._readAttributes(deps),

      callOnDestroy: hasLifecycleHook(onDestroy, rb.key.token, ann),
      callOnChange: hasLifecycleHook(onChange, rb.key.token, ann),
      callOnCheck: hasLifecycleHook(onCheck, rb.key.token, ann),
      callOnInit: hasLifecycleHook(onInit, rb.key.token, ann),
      callOnAllChangesDone: hasLifecycleHook(onAllChangesDone, rb.key.token, ann),

      changeDetection: ann instanceof
          Component ? ann.changeDetection : null
    });
    return new DirectiveBinding(rb.key, rb.factory, deps, rb.providedAsPromise,
                                resolvedAppInjectables, resolvedHostInjectables,
                                resolvedViewInjectables, metadata);
  }


static _readAttributes(deps) {
    var readAttributes = [];
    ListWrapper.forEach(deps, (dep) => {
      if (isPresent(dep.attributeName)) {
        ListWrapper.push(readAttributes, dep.attributeName);
      }
    });
    return readAttributes;
  }


static createFromType(type: Type, annotation: Directive): DirectiveBinding {
    var binding = new Binding(type, {toClass: type});
    return DirectiveBinding.createFromBinding(binding, annotation);
  }


subscribe(view: viewModule.AppView, boundElementIndex: number, directive: Object) {
    var eventEmitter = this.getter(directive);
    return ObservableWrapper.subscribe(
        eventEmitter,
        eventObj => view.triggerEventHandlers(this.eventName, eventObj, boundElementIndex));
  }


subscribe(view: viewModule.AppView, boundElementIndex: number, directive: Object) {
    var eventEmitter = this.getter(directive);
    return ObservableWrapper.subscribe(
        eventEmitter,
        actionObj => view.callAction(boundElementIndex, this.actionExpression, actionObj));
  }


getKeyId() { return this.binding.key.id; }


createEventEmitterAccessors() {
    if (!(this.binding instanceof DirectiveBinding)) return [];
    var db = <DirectiveBinding>this.binding;
    return ListWrapper.map(db.eventEmitters, eventName => new EventEmitterAccessor(
                                                 eventName, reflector.getter(eventName)));
  }


createHostActionAccessors() {
    if (!(this.binding instanceof DirectiveBinding)) return [];
    var res = [];
    var db = <DirectiveBinding>this.binding;
    MapWrapper.forEach(
        db.hostActions,
        (actionExpression, actionName) => {ListWrapper.push(
            res, new HostActionAccessor(actionExpression, reflector.getter(actionName)))});
    return res;
  }


static create(parent: ProtoElementInjector, index: number, bindings: List<ResolvedBinding>,
                firstBindingIsComponent: boolean, distanceToParent: number) {
    var bd = [];

    ProtoElementInjector._createDirectiveBindingData(bindings, bd, firstBindingIsComponent);
    ProtoElementInjector._createHostInjectorBindingData(bindings, bd);
    if (firstBindingIsComponent) {
      ProtoElementInjector._createViewInjectorBindingData(bindings, bd);
    }

    return new ProtoElementInjector(parent, index, bd, distanceToParent, firstBindingIsComponent);
  }


private static _createDirectiveBindingData(bindings: List<ResolvedBinding>, bd: List<BindingData>,
                                             firstBindingIsComponent: boolean) {
    if (firstBindingIsComponent) {
      ListWrapper.push(bd, new BindingData(bindings[0], LIGHT_DOM_AND_SHADOW_DOM));
      for (var i = 1; i < bindings.length; ++i) {
        ListWrapper.push(bd, new BindingData(bindings[i], LIGHT_DOM));
      }
    } else {
      ListWrapper.forEach(bindings, b => {ListWrapper.push(bd, new BindingData(b, LIGHT_DOM))});
    }
  }


private static _createHostInjectorBindingData(bindings: List<ResolvedBinding>,
                                                bd: List<BindingData>) {
    ListWrapper.forEach(bindings, b => {
      ListWrapper.forEach(b.resolvedHostInjectables, b => {
        ListWrapper.push(bd, new BindingData(ProtoElementInjector._createBinding(b), LIGHT_DOM));
      });
    });
  }


private static _createViewInjectorBindingData(bindings: List<ResolvedBinding>,
                                                bd: List<BindingData>) {
    var db = <DirectiveBinding>bindings[0];
    ListWrapper.forEach(
        db.resolvedViewInjectables,
        b => ListWrapper.push(bd,
                              new BindingData(ProtoElementInjector._createBinding(b), SHADOW_DOM)));
  }


private static _createBinding(b: ResolvedBinding) {
    var deps = ListWrapper.map(b.dependencies, d => DependencyWithVisibility.createFrom(d));
    return new ResolvedBinding(b.key, b.factory, deps, b.providedAsPromise);
  }


instantiate(parent: ElementInjector): ElementInjector {
    return new ElementInjector(this, parent);
  }


directParent(): ProtoElementInjector { return this.distanceToParent < 2 ? this.parent : null; }


getBindingAtIndex(index: number): any { return this._strategy.getBindingAtIndex(index); }


hasBindings(): boolean { return isPresent(this._binding0); }