/* eslint-disable no-dupe-class-members */
export class Fraction {
  public numerator: number;
  public denominator: number;

  constructor(numerator: number, denominator: number) {
    if (denominator === 0) {
      throw new Error('Denominator cannot be zero');
    }
    this.numerator = numerator;
    this.denominator = denominator;
  }

  public toString(): string {
    return `${this.numerator} / ${this.denominator}`;
  }

  public toDecimal(): number {
    return this.numerator / this.denominator;
  }

  public toInteger(): number {
    return Math.floor(this.numerator / this.denominator);
  }

  public reduce(): Fraction {
    const g = Fraction.gcd(this.numerator, this.denominator);
    return new Fraction(this.numerator / g, this.denominator / g);
  }

  public isIrreducible(): boolean {
    return Fraction.gcd(this.numerator, this.denominator) === 1;
  }

  public isDivisible(): boolean {
    return this.numerator % this.denominator === 0;
  }

  public static add(l: Fraction, r: Fraction): Fraction;
  public static add(l: number, r: Fraction): Fraction;
  public static add(l: Fraction, r: number): Fraction;
  public static add(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.denominator + r.numerator * l.denominator, l.denominator * r.denominator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.denominator + r.numerator, r.denominator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator + r * l.denominator, l.denominator).reduce();
    }
    return new Fraction((l as number) + (r as number), 1);
  }

  public static sub(l: Fraction, r: Fraction): Fraction;
  public static sub(l: number, r: Fraction): Fraction;
  public static sub(l: Fraction, r: number): Fraction;
  public static sub(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.denominator - r.numerator * l.denominator, l.denominator * r.denominator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.denominator - r.numerator, r.denominator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator - r * l.denominator, l.denominator).reduce();
    }
    return new Fraction((l as number) - (r as number), 1);
  }

  public static mul(l: Fraction, r: Fraction): Fraction;
  public static mul(l: number, r: Fraction): Fraction;
  public static mul(l: Fraction, r: number): Fraction;
  public static mul(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.numerator, l.denominator * r.denominator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.numerator, r.denominator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator * r, l.denominator).reduce();
    }
    return new Fraction((l as number) * (r as number), 1);
  }

  public static div(l: Fraction, r: Fraction): Fraction;
  public static div(l: number, r: Fraction): Fraction;
  public static div(l: Fraction, r: number): Fraction;
  public static div(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.denominator, l.denominator * r.numerator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.denominator, r.numerator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator, l.denominator * r).reduce();
    }
    return new Fraction((l as number), (r as number)).reduce();
  }

  private static gcd(a: number, b: number): number {
    if (a < b) {
      return Fraction.gcd(b, a);
    } else if (b === 0) {
      return a;
    }
    return Fraction.gcd(b, a % b);
  }
}


public toString(): string {
    return `${this.numerator} / ${this.denominator}`;
  }


public toDecimal(): number {
    return this.numerator / this.denominator;
  }


public toInteger(): number {
    return Math.floor(this.numerator / this.denominator);
  }


public reduce(): Fraction {
    const g = Fraction.gcd(this.numerator, this.denominator);
    return new Fraction(this.numerator / g, this.denominator / g);
  }


public isIrreducible(): boolean {
    return Fraction.gcd(this.numerator, this.denominator) === 1;
  }


public isDivisible(): boolean {
    return this.numerator % this.denominator === 0;
  }


public static add(l: Fraction, r: Fraction): Fraction;


public static add(l: number, r: Fraction): Fraction;


public static add(l: Fraction, r: number): Fraction;


public static add(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.denominator + r.numerator * l.denominator, l.denominator * r.denominator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.denominator + r.numerator, r.denominator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator + r * l.denominator, l.denominator).reduce();
    }
    return new Fraction((l as number) + (r as number), 1);
  }


public static sub(l: Fraction, r: Fraction): Fraction;


public static sub(l: number, r: Fraction): Fraction;


public static sub(l: Fraction, r: number): Fraction;


public static sub(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.denominator - r.numerator * l.denominator, l.denominator * r.denominator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.denominator - r.numerator, r.denominator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator - r * l.denominator, l.denominator).reduce();
    }
    return new Fraction((l as number) - (r as number), 1);
  }


public static mul(l: Fraction, r: Fraction): Fraction;


public static mul(l: number, r: Fraction): Fraction;


public static mul(l: Fraction, r: number): Fraction;


public static mul(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.numerator, l.denominator * r.denominator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.numerator, r.denominator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator * r, l.denominator).reduce();
    }
    return new Fraction((l as number) * (r as number), 1);
  }


public static div(l: Fraction, r: Fraction): Fraction;


public static div(l: number, r: Fraction): Fraction;


public static div(l: Fraction, r: number): Fraction;


public static div(l: Fraction | number, r: Fraction | number): Fraction {
    if (typeof l !== 'number' && typeof r !== 'number') {
      return new Fraction(l.numerator * r.denominator, l.denominator * r.numerator).reduce();
    } else if (typeof l === 'number' && typeof r !== 'number') {
      return new Fraction(l * r.denominator, r.numerator).reduce();
    } else if (typeof l !== 'number'  && typeof r === 'number') {
      return new Fraction(l.numerator, l.denominator * r).reduce();
    }
    return new Fraction((l as number), (r as number)).reduce();
  }


private static gcd(a: number, b: number): number {
    if (a < b) {
      return Fraction.gcd(b, a);
    } else if (b === 0) {
      return a;
    }
    return Fraction.gcd(b, a % b);
  }


(args) => <Footer {...args} />


@NgModule({
  imports: [
    CommonModule,
    HttpClientModule,
    TranslateModule,
    RouterModule
  ],
  providers: [
    {
      provide: HttpClient,
      useClass: HttpService
    },
    {
      provide: RouteReuseStrategy,
      useClass: RouteReusableStrategy
    },
    ...providers
  ]
})
export class CoreModule {

  constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
    // Import guard
    if (parentModule) {
      throw new Error(`${parentModule} has already been loaded. Import Core module in the AppModule only.`);
    }
  }

}


export default class Angular extends Command {
  public static templateName = '@coveo/angular';
  /**
   * Requirements Based on https://angular.io/guide/setup-local
   * and https://www.npmjs.com/package/@angular/cli package.json engines section.
   */
  public static requiredNodeVersion = '>=12.13.0 <15.0.0';
  public static requiredNpmVersion = '^6.11.0 || ^7.5.6';

  public static description =
    'Create a Coveo Headless-powered search page with the Angular web framework. See <https://docs.coveo.com/headless> and <https://angular.io/>.';

  public static flags = {
    version: flags.string({
      char: 'v',
      description: `The version of ${Angular.templateName} to use.`,
      default: getPackageVersion(Angular.templateName),
    }),
    defaults: flags.boolean({
      char: 'd',
      description:
        'Whether to automatically select the default value for all prompts that have a default value.',
    }),
  };

  public static args = [
    {
      name: 'name',
      description: 'The name of the application to create.',
      required: true,
    },
  ];

  @Preconditions(
    IsAuthenticated(),
    IsNodeVersionInRange(Angular.requiredNodeVersion),
    IsNpmVersionInRange(Angular.requiredNpmVersion),
    IsNgInstalled()
  )
  public async run() {
    const {args, flags} = this.parse(Angular);
    await this.createProject(args.name, flags.defaults);
    await this.addCoveoToProject(args.name, flags.defaults);
    this.displayFeedbackAfterSuccess(args.name);
    await this.config.runHook(
      'analytics',
      buildAnalyticsSuccessHook(this, flags)
    );
  }

  private async createProject(name: string, defaults: boolean) {
    const cliArgs = ['new', name, '--style', 'scss', '--routing'];

    if (defaults) {
      cliArgs.push('--defaults');
    }

    return this.runAngularCliCommand(cliArgs);
  }

  private async addCoveoToProject(applicationName: string, defaults: boolean) {
    const cfg = await this.configuration.get();
    const args = this.args;
    const authenticatedClient = new AuthenticatedClient();
    const userInfo = await authenticatedClient.getUserInfo();
    const apiKey = await authenticatedClient.createImpersonateApiKey(args.name);
    const flags = this.flags;
    const schematicVersion =
      flags.version || getPackageVersion(Angular.templateName);

    const cliArgs = [
      'add',
      `${Angular.templateName}@${schematicVersion}`,
      '--org-id',
      cfg.organization,
      '--api-key',
      apiKey.value!,
      '--platform-url',
      platformUrl({environment: cfg.environment}),
      '--user',
      userInfo.providerUsername,
      '--skip-confirmation',
    ];

    if (defaults) {
      cliArgs.push('--defaults');
    }

    return this.runAngularCliCommand(cliArgs, {cwd: applicationName});
  }

  private runAngularCliCommand(args: string[], options = {}) {
    return spawnProcess(appendCmdIfWindows`ng`, args, options);
  }

  public async catch(err?: Error) {
    const flags = this.flags;
    await this.config.runHook(
      'analytics',
      buildAnalyticsFailureHook(this, flags, err)
    );
    throw err;
  }

  private get configuration() {
    return new Config(this.config.configDir, this.error);
  }

  private get flags() {
    const {flags} = this.parse(Angular);
    return flags;
  }

  private get args() {
    const {args} = this.parse(Angular);
    return args;
  }

  private displayFeedbackAfterSuccess(name: string) {
    this.log(`
    To get started:

    cd ${name}
    npm run start

    See package.json for other available commands.
    Happy hacking!
    `);
  }
}


@Preconditions(
    IsAuthenticated(),
    IsNodeVersionInRange(Angular.requiredNodeVersion),
    IsNpmVersionInRange(Angular.requiredNpmVersion),
    IsNgInstalled()
  )
  public async run() {
    const {args, flags} = this.parse(Angular);
    await this.createProject(args.name, flags.defaults);
    await this.addCoveoToProject(args.name, flags.defaults);
    this.displayFeedbackAfterSuccess(args.name);
    await this.config.runHook(
      'analytics',
      buildAnalyticsSuccessHook(this, flags)
    );
  }


private async createProject(name: string, defaults: boolean) {
    const cliArgs = ['new', name, '--style', 'scss', '--routing'];

    if (defaults) {
      cliArgs.push('--defaults');
    }

    return this.runAngularCliCommand(cliArgs);
  }


private async addCoveoToProject(applicationName: string, defaults: boolean) {
    const cfg = await this.configuration.get();
    const args = this.args;
    const authenticatedClient = new AuthenticatedClient();
    const userInfo = await authenticatedClient.getUserInfo();
    const apiKey = await authenticatedClient.createImpersonateApiKey(args.name);
    const flags = this.flags;
    const schematicVersion =
      flags.version || getPackageVersion(Angular.templateName);

    const cliArgs = [
      'add',
      `${Angular.templateName}@${schematicVersion}`,
      '--org-id',
      cfg.organization,
      '--api-key',
      apiKey.value!,
      '--platform-url',
      platformUrl({environment: cfg.environment}),
      '--user',
      userInfo.providerUsername,
      '--skip-confirmation',
    ];

    if (defaults) {
      cliArgs.push('--defaults');
    }

    return this.runAngularCliCommand(cliArgs, {cwd: applicationName});
  }


private runAngularCliCommand(args: string[], options = {}) {
    return spawnProcess(appendCmdIfWindows`ng`, args, options);
  }


public async catch(err?: Error) {
    const flags = this.flags;
    await this.config.runHook(
      'analytics',
      buildAnalyticsFailureHook(this, flags, err)
    );
    throw err;
  }


private displayFeedbackAfterSuccess(name: string) {
    this.log(`
    To get started:

    cd ${name}
    npm run start

    See package.json for other available commands.
    Happy hacking!
    `);
  }


/**
 * @memberof dep.app
 * @ngdoc service
 * @name Client
 * @description
 * Client Model
 */
export class Client implements IClient {
    public clients: IClientInfo[];
    public clientManagerEnabled: boolean;
    public clientManagerClientsAvailable: boolean;
    public clientManagerClientSelected: IClientInfo;
    public firmId: string;
    constructor(client: IClient) {
        this.clients = client.clients;
        this.clientManagerEnabled = client.clientManagerEnabled;
        this.clientManagerClientsAvailable = client.clientManagerClientsAvailable;
        this.clientManagerClientSelected = client.clientManagerClientSelected;
        this.firmId = client.firmId;
    }
}


'$auth.user'(newUser) {
      if (newUser === false) {
        this.$router.push(`/`)
      }
    }


async mounted() {
    // has to be on mounted as needs to be checked client-side
    if (!this.$cwa.isAdmin) {
      // this is so any components on the page that may be changing the URL eg remove
      // the search filters that will be doing this for a list
      await this.$destroy(this.$root)
      // now we have no interference - we redirect.
      await this.$router.push('/login')
      return
    }
    this.$cwa.$eventBus.$emit(ADMIN_BAR_EVENTS.changeView, 'admin')
  }


export function ActiveLink({children, activeClassName, ...rest}: ActiveLinkProps) {

  const { asPath } = useRouter();

  const className = asPath === rest.href ? activeClassName : ""

  // na aula foi utilizado o clone Element.
  /*
  CloneElement(children, {
    className
  })
  */

  return (
    <Link {...rest} >
      {children}
    </Link>
  )
}


interface ActiveLinkProps extends LinkProps {
  children: ReactNode; // na aula foi utilizado o ReactElement
  activeClassName: string;
}


c => c.clone()


export class LcEntryPointValue extends LcEntryGeometryValue<LcEntryPointChange> {
  readonly kind = LcEntryType.POINT;

  public x: number;
  public y: number;

  public constructor(
    id: string | null,
    lcEntry: LcEntry | null,
    media: string | null,
    labelIteration: LabelIteration | string | null,
    timestamp: number | null,
    labeler: string | null,
    children: LcEntryValue[] | null,
    parent: string | null,
    valid: boolean,
    x: number,
    y: number,
    comment?: string | null
  ) {
    super(id, lcEntry, media, labelIteration, timestamp, labeler, children, parent, valid, comment);
    this.x = x;
    this.y = y;
  }

  /**
   * Create a fully copy of the current LcEntryPolyValue.
   */
  public createClone(): LcEntryPointValue {
    return new LcEntryPointValue(
      this.id,
      this.lcEntry,
      this.mediaId,
      this.labelIteration,
      this.timestamp,
      this.labeler,
      this.children.map(c => c.clone()),
      this.lcEntryValueParentId,
      this.valid,
      this.x,
      this.y,
      this.comment
    );
  }

  public withChange(change: LcEntryPointChange): LcEntryPointValue {
    const clone = this.clone() as LcEntryPointValue;
    if (!!change) {
      clone.x = change.x;
      clone.y = change.y;
    }
    return clone;
  }

  /**
   * Create a fully copy of the current LcEntryRadioValue.
   */
  protected eatValues(source: LcEntryPointValue): void {
    this.x = source.x;
    this.y = source.y;
    this.comment = source.comment;
  }

  public hasCoordinates(): boolean {
    // check if the points x & y are set.
    return !(
      (!this.x === null) ||
      (!this.y === null)
    );
  }

  protected isThisEntryValid(): boolean {
    return this.x !== undefined && this.y !== undefined;
  }

  public move(offset: Point): void {
    this.x += offset.x;
    this.y += offset.y;
  }

  public getDistance(point: Point): Point {
    return new Point(this.x - point.x, this.y - point.y);
  }

  public outOfBounds(size: Point): boolean {
    return this.x > size.x || this.x < 0 ||
      this.y > size.y || this.y < 0;
  }

  public getBoundingBox(): Point {
    return new Point(1, 1);
  }
}


/**
   * Create a fully copy of the current LcEntryPolyValue.
   */
public createClone(): LcEntryPointValue {
    return new LcEntryPointValue(
      this.id,
      this.lcEntry,
      this.mediaId,
      this.labelIteration,
      this.timestamp,
      this.labeler,
      this.children.map(c => c.clone()),
      this.lcEntryValueParentId,
      this.valid,
      this.x,
      this.y,
      this.comment
    );
  }


public withChange(change: LcEntryPointChange): LcEntryPointValue {
    const clone = this.clone() as LcEntryPointValue;
    if (!!change) {
      clone.x = change.x;
      clone.y = change.y;
    }
    return clone;
  }


/**
   * Create a fully copy of the current LcEntryRadioValue.
   */
protected eatValues(source: LcEntryPointValue): void {
    this.x = source.x;
    this.y = source.y;
    this.comment = source.comment;
  }


public hasCoordinates(): boolean {
    // check if the points x & y are set.
    return !(
      (!this.x === null) ||
      (!this.y === null)
    );
  }


protected isThisEntryValid(): boolean {
    return this.x !== undefined && this.y !== undefined;
  }


public move(offset: Point): void {
    this.x += offset.x;
    this.y += offset.y;
  }


public getDistance(point: Point): Point {
    return new Point(this.x - point.x, this.y - point.y);
  }


public outOfBounds(size: Point): boolean {
    return this.x > size.x || this.x < 0 ||
      this.y > size.y || this.y < 0;
  }


public getBoundingBox(): Point {
    return new Point(1, 1);
  }


/** @type {import('@sveltejs/kit').RequestHandler} */
export async function get() {
  return {
    status: 302,
    headers: {
      Location: '/about',
    },
  }
}


({ days }) => {
    return (

            {days.map((day) =>
                day.hour.map((hour, i) => (

                        {dateformat(hour.time_epoch * 1000, "htt")} {hour.temp_c}°C


(day) =>
                day.hour.map((hour, i) => (

                        {dateformat(hour.time_epoch * 1000, "htt")


(hour, i) => (
                    <div key={i}>
                        <img src


interface ForecastProps {
    days: ForecastDay[];
}


dateformat(hour


(resp: PasteModel) => {
                let formattedResponse: [PasteModel, string] = [resp, null];
                return formattedResponse;
            }


(paste: PasteModel) => {
                this.pasteMap.set(paste.quick_link, Observable.of(paste));
                let formattedResponse: [PasteModel, string] = [paste, null];
                return formattedResponse;
            }


(error: HttpErrorResponse) => {
                let formattedResponse: [PasteModel, string] = [null, 'An unexpected error occured'];

                if (error.status === 404) {
                    formattedResponse[1] = 'Paste not found';
                } else if (error.status === 401) {
                    formattedResponse[1] = 'This paste is private';
                }
                return Observable.of(formattedResponse);
            }


(pastes: PasteModel[]) => {
                pastes.forEach(paste => {
                    this.pasteMap.set(paste.quick_link, Observable.of(paste));
                });
            }


paste => {
                    this.pasteMap.set(paste.quick_link, Observable.of(paste));
                }


(paste: PasteModel) => this.pasteMap.set(paste.quick_link, Observable.of(paste))


(error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(null);
            }


(resp: any) => {
                return true;
            }


(error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(false);
            }


(deletedPaste: PasteModel) => {
                this.pasteMap.delete(deletedPaste.quick_link);
                return true;
            }


(error: HttpErrorResponse) => {
                return Observable.of(false);
            }


@Injectable()
export class PasteService {

    /**
     * A map used to cache Pastes and retrieve them from memory
     */
    pasteMap: Map<string, Observable<PasteModel>> = new Map<string, Observable<PasteModel>>();

    constructor(private http: HttpClient, private authService: AuthService) { }

    /**
     * Gets the paste with the specified quickLink. The response is formatted into an array with a PasteModel and string for error message.
     * If the paste could not be retrieved, the 1st element of the array is set to null and the 2nd element contains an error message.
     * If the paste was found, the 1st element contains the PasteModel and the 2nd element is set to null
     *
     * @param {string} quickLink
     * @returns {Observable<[PasteModel?, string?]>}
     * @memberof PasteService
     */
    getPaste(quickLink: string): Observable<[PasteModel, string]> {
        let cachedPaste = this.pasteMap.get(quickLink)
        if (cachedPaste) {
            return cachedPaste.map((resp: PasteModel) => {
                let formattedResponse: [PasteModel, string] = [resp, null];
                return formattedResponse;
            });
        }
        return this.http.get<PasteModel>(`/api/pastes/${quickLink}?isLink=true`, { headers: this.authService.authHeader() })
            .map((paste: PasteModel) => {
                this.pasteMap.set(paste.quick_link, Observable.of(paste));
                let formattedResponse: [PasteModel, string] = [paste, null];
                return formattedResponse;
            })
            .catch((error: HttpErrorResponse) => {
                let formattedResponse: [PasteModel, string] = [null, 'An unexpected error occured'];

                if (error.status === 404) {
                    formattedResponse[1] = 'Paste not found';
                } else if (error.status === 401) {
                    formattedResponse[1] = 'This paste is private';
                }
                return Observable.of(formattedResponse);
            });

    }

    /**
     * Get the list of all paste models, returning an array of PasteModels on success, or null on failure
     *
     * @param {number} [pageNumber=1]
     * @param {number} [limit=10]
     * @returns {Observable<PasteModel[]>}
     * @memberof PasteService
     */
    getPastes(pageNumber: number = 1, limit: number = 10): Observable<PasteModel[]> {

        return this.http.get<PasteModel[]>(`/api/pastes?pageNumber=${pageNumber}&limit=${limit}`, { headers: this.authService.authHeader() })
            .do((pastes: PasteModel[]) => {
                pastes.forEach(paste => {
                    this.pasteMap.set(paste.quick_link, Observable.of(paste));
                });
            });

    }

    /**
     * Create a new paste and return the created PasteModel on success, or null on failure
     *
     * @param {PasteCreateModel} pasteToCreate
     * @returns {Observable<PasteModel?>}
     * @memberof PasteService
     */
    createPaste(pasteToCreate: PasteCreateModel): Observable<PasteModel> {
        let formData = new FormData();
        if (pasteToCreate.file_to_upload !== null) {
            formData.append('file_to_upload', pasteToCreate.file_to_upload[0]);
        }
        formData.append('is_public', pasteToCreate.is_public + "");
        formData.append('message', pasteToCreate.message);
        formData.append('quick_link', pasteToCreate.quick_link);

        return this.http.post<PasteModel>("api/pastes", formData, { headers: this.authService.authHeader() })
            .do((paste: PasteModel) => this.pasteMap.set(paste.quick_link, Observable.of(paste)))
            .publishReplay(1)
            .refCount()
            .catch((error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(null);
            });
    }

    /**
     * Generates a random alphanumeric string
     *
     * @returns {string}
     * @memberof PasteService
     */
    generateRandomPasteLink(): string {
        let link = "";
        let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let length = 32;

        for (let i = 0; i < length; i++) {
            link += charset.charAt(Math.floor(Math.random() * charset.length));
        }
        return link;
    }

    /**
     *  Create a link which can be used to view a blob for the paste that is normally private. After durationInMinutes has passed, the link is no longer valid
     *
     * @param {string} pasteId
     * @param {number} durationInMinutes
     * @returns {Observable<SharedLinkModel>}
     * @memberof PasteService
     */
    createSharedLink(pasteId: string, durationInMinutes: number): Observable<SharedLinkModel> {
        let sharedLinkInput: SharedLinkCreateModel = { duration_in_minutes: durationInMinutes };

        return this.http.post<SharedLinkModel>(`/api/pastes/${pasteId}/sharedlinks`, sharedLinkInput, { headers: this.authService.authHeader() })
            .catch((error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(null);
            });
    }

    /**
     * Create a paste anonymously using an upload link
     *
     * @param {string} quickLink
     * @param {AnonPasteCreateModel} pasteToCreate
     * @returns {Observable<boolean>}
     * @memberof PasteService
     */
    createPasteFromLink(quickLink: string, pasteToCreate: AnonPasteCreateModel): Observable<boolean> {
        let formData = new FormData();
        if (pasteToCreate.file_to_upload !== null) {
            formData.append('file_to_upload', pasteToCreate.file_to_upload[0]);
        }
        formData.append('message', pasteToCreate.message);

        return this.http.post(`api/pastes/${quickLink}`, formData, { headers: this.authService.authHeader() })
            .map((resp: any) => {
                return true;
            })
            .publishReplay(1)
            .refCount()
            .catch((error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(false);
            });
    }

    /**
     * Deletes a paste with the given id
     *
     * @param {string} id
     * @returns {Observable<boolean>}
     * @memberof PasteService
     */
    deletePaste(id: string): Observable<boolean> {

        return this.http.delete(`/api/pastes/${id}`, { headers: this.authService.authHeader() })
            .map((deletedPaste: PasteModel) => {
                this.pasteMap.delete(deletedPaste.quick_link);
                return true;
            })
            .catch((error: HttpErrorResponse) => {
                return Observable.of(false);
            });

    }

}


/**
     * Gets the paste with the specified quickLink. The response is formatted into an array with a PasteModel and string for error message.
     * If the paste could not be retrieved, the 1st element of the array is set to null and the 2nd element contains an error message.
     * If the paste was found, the 1st element contains the PasteModel and the 2nd element is set to null
     *
     * @param {string} quickLink
     * @returns {Observable<[PasteModel?, string?]>}
     * @memberof PasteService
     */
getPaste(quickLink: string): Observable<[PasteModel, string]> {
        let cachedPaste = this.pasteMap.get(quickLink)
        if (cachedPaste) {
            return cachedPaste.map((resp: PasteModel) => {
                let formattedResponse: [PasteModel, string] = [resp, null];
                return formattedResponse;
            });
        }
        return this.http.get<PasteModel>(`/api/pastes/${quickLink}?isLink=true`, { headers: this.authService.authHeader() })
            .map((paste: PasteModel) => {
                this.pasteMap.set(paste.quick_link, Observable.of(paste));
                let formattedResponse: [PasteModel, string] = [paste, null];
                return formattedResponse;
            })
            .catch((error: HttpErrorResponse) => {
                let formattedResponse: [PasteModel, string] = [null, 'An unexpected error occured'];

                if (error.status === 404) {
                    formattedResponse[1] = 'Paste not found';
                } else if (error.status === 401) {
                    formattedResponse[1] = 'This paste is private';
                }
                return Observable.of(formattedResponse);
            });

    }


/**
     * Get the list of all paste models, returning an array of PasteModels on success, or null on failure
     *
     * @param {number} [pageNumber=1]
     * @param {number} [limit=10]
     * @returns {Observable<PasteModel[]>}
     * @memberof PasteService
     */
getPastes(pageNumber: number = 1, limit: number = 10): Observable<PasteModel[]> {

        return this.http.get<PasteModel[]>(`/api/pastes?pageNumber=${pageNumber}&limit=${limit}`, { headers: this.authService.authHeader() })
            .do((pastes: PasteModel[]) => {
                pastes.forEach(paste => {
                    this.pasteMap.set(paste.quick_link, Observable.of(paste));
                });
            });

    }


/**
     * Create a new paste and return the created PasteModel on success, or null on failure
     *
     * @param {PasteCreateModel} pasteToCreate
     * @returns {Observable<PasteModel?>}
     * @memberof PasteService
     */
createPaste(pasteToCreate: PasteCreateModel): Observable<PasteModel> {
        let formData = new FormData();
        if (pasteToCreate.file_to_upload !== null) {
            formData.append('file_to_upload', pasteToCreate.file_to_upload[0]);
        }
        formData.append('is_public', pasteToCreate.is_public + "");
        formData.append('message', pasteToCreate.message);
        formData.append('quick_link', pasteToCreate.quick_link);

        return this.http.post<PasteModel>("api/pastes", formData, { headers: this.authService.authHeader() })
            .do((paste: PasteModel) => this.pasteMap.set(paste.quick_link, Observable.of(paste)))
            .publishReplay(1)
            .refCount()
            .catch((error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(null);
            });
    }


/**
     * Generates a random alphanumeric string
     *
     * @returns {string}
     * @memberof PasteService
     */
generateRandomPasteLink(): string {
        let link = "";
        let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        let length = 32;

        for (let i = 0; i < length; i++) {
            link += charset.charAt(Math.floor(Math.random() * charset.length));
        }
        return link;
    }


/**
     *  Create a link which can be used to view a blob for the paste that is normally private. After durationInMinutes has passed, the link is no longer valid
     *
     * @param {string} pasteId
     * @param {number} durationInMinutes
     * @returns {Observable<SharedLinkModel>}
     * @memberof PasteService
     */
createSharedLink(pasteId: string, durationInMinutes: number): Observable<SharedLinkModel> {
        let sharedLinkInput: SharedLinkCreateModel = { duration_in_minutes: durationInMinutes };

        return this.http.post<SharedLinkModel>(`/api/pastes/${pasteId}/sharedlinks`, sharedLinkInput, { headers: this.authService.authHeader() })
            .catch((error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(null);
            });
    }


/**
     * Create a paste anonymously using an upload link
     *
     * @param {string} quickLink
     * @param {AnonPasteCreateModel} pasteToCreate
     * @returns {Observable<boolean>}
     * @memberof PasteService
     */
createPasteFromLink(quickLink: string, pasteToCreate: AnonPasteCreateModel): Observable<boolean> {
        let formData = new FormData();
        if (pasteToCreate.file_to_upload !== null) {
            formData.append('file_to_upload', pasteToCreate.file_to_upload[0]);
        }
        formData.append('message', pasteToCreate.message);

        return this.http.post(`api/pastes/${quickLink}`, formData, { headers: this.authService.authHeader() })
            .map((resp: any) => {
                return true;
            })
            .publishReplay(1)
            .refCount()
            .catch((error: HttpErrorResponse) => {
                console.log(error.status);
                console.log(error.error);
                return Observable.of(false);
            });
    }


/**
     * Deletes a paste with the given id
     *
     * @param {string} id
     * @returns {Observable<boolean>}
     * @memberof PasteService
     */
deletePaste(id: string): Observable<boolean> {

        return this.http.delete(`/api/pastes/${id}`, { headers: this.authService.authHeader() })
            .map((deletedPaste: PasteModel) => {
                this.pasteMap.delete(deletedPaste.quick_link);
                return true;
            })
            .catch((error: HttpErrorResponse) => {
                return Observable.of(false);
            });

    }


interface ChartObject extends Highcharts.ChartObject {
        options: Options;
    }


interface NavigatorOptions {
        adaptToUpdatedData?: boolean;
        baseSeries?: string | number;
        enabled?: boolean;
        handles?: {
            backgroundColor?: string;
            borderColor?: string;
        };
        height?: number;
        margin?: number;
        maskFill?: string;
        maskInside?: boolean;
        outlineColor?: string;
        outlineWidth?: number;
        series?: Highcharts.IndividualSeriesOptions;
        xAxis?: Highcharts.AxisOptions;
        yAxis?: Highcharts.AxisOptions;
    }


interface RangeSelectorButton {
        type: string; // Defines the timespan, can be one of 'millisecond', 'second', 'minute', 'day', 'week', 'month', 'ytd' (year to date), 'year' and 'all'.
        count?: number;
        text: string;
        dataGrouping?: any; // not sure how this works
    }


interface RangeSelectorOptions {
        allButtonsEnabled?: boolean;
        buttonSpacing?: number;
        buttonTheme?: any;
        buttons?: RangeSelectorButton[];
        enabled?: boolean;
        inputBoxBorderColor?: string;
        inputBoxHeight?: number;
        inputBoxWidth?: number;
        inputDateFormat?: string;
        inputDateParser?(date: string): number;
        inputEditDateFormat?: string;
        inputEnabled?: boolean;
        inputPosition?: {
            align?: string;
            verticalAlign?: string;
            x?: number;
            y?: number;
        };
        inputStyle?: Highcharts.CSSObject;
        labelStyle?: Highcharts.CSSObject;
        selected?: number;
    }


interface ScrollbarOptions {
        barBackgroundColor?: string;
        barBorderColor?: string;
        barBorderRadius?: number;
        barBorderWidth?: number;
        buttonArrowColor?: string;
        buttonBackgroundColor?: string;
        buttonBorderColor?: string;
        buttonBorderRadius?: number;
        buttonBorderWidth?: number;
        enabled?: boolean;
        height?: number;
        liveRedraw?: boolean;
        minWidth?: number;
        rifleColor?: string;
        trackBackgroundColor?: string;
        trackBorderColor?: string;
        trackBorderRadius?: number;
        trackBorderWidth?: number;
    }


interface AxisOptions extends Highcharts.AxisOptions {
        scrollbar?: ScrollbarOptions;
    }


interface Options extends Highcharts.Options {
        navigator?: NavigatorOptions;
        rangeSelector?: RangeSelectorOptions;
        scrollbar?: ScrollbarOptions;
        xAxis?: AxisOptions[] | AxisOptions;
        yAxis?: AxisOptions[] | AxisOptions;
    }


interface Chart {
        new (options: Options): ChartObject;
        new (options: Options, callback: (chart: ChartObject) => void): ChartObject;
    }


interface Static extends Highcharts.Static {
        StockChart: Chart;
        stockChart(renderTo: string | HTMLElement, options: Options, callback?: (chart: ChartObject) => void): ChartObject;
    }


interface JQuery {
        highcharts(type: "StockChart"): Highstock.ChartObject;
        /**
         * Creates a new Highcharts.Chart for the current JQuery selector; usually
         * a div selected by $('#container')
         * @param {Highcharts.Options} options Options for this chart
         * @return current {JQuery} selector the current JQuery selector
         */
        highcharts(type: "StockChart", options: Highstock.Options): JQuery;
        /**
         * Creates a new Highcharts.Chart for the current JQuery selector; usually
         * a div selected by $('#container')
         * @param {Highcharts.Options} options Options for this chart
         * @param callback Callback function used to manipulate the constructed chart instance
         * @return current {JQuery} selector the current JQuery selector
         */
        highcharts(type: "StockChart", options: Highstock.Options, callback: (chart: Highstock.ChartObject) => void): JQuery;

        highcharts(type: string): Highcharts.ChartObject;
        highcharts(type: string, options: Highcharts.Options): JQuery;
        highcharts(type: string, options: Highcharts.Options, callback: (chart: Highcharts.ChartObject) => void): JQuery;
    }


export async function DnsResolve4(hostname:string) : Promise<string>{
    if(net.isIP(hostname)){
        return hostname;
    }else{ //if dst_address is a domain, resolve it firstly

        let resolved = await dns.promises.lookup(hostname,4);
        return resolved.address;
    }
}


dataLoaded => {
      if (dataLoaded) {
        this.topoData = this.bdService.getTopologyData(this.dataService.contivData);
        this.topoData.type = 'bd';
        this.domain = this.dataService.contivData.contivData[0];
        this.bdId = this.domain.bd[0].name;

        const topo: TopologyDataModel = new TopologyDataModel();
        topo.setData(this.topoData.nodes, this.topoData.links);
        this.topologyService.setTopologyData(topo);
      }
    }


() => this.tableType = 'summary'


() => this.tableType = 'pods'


() => this.tableType = 'tunnels'


s => s.unsubscribe


@Component({
  selector: 'app-bridge-domain',
  templateUrl: './bridge-domain.component.html',
  styleUrls: ['./bridge-domain.component.css']
})
export class BridgeDomainComponent implements OnInit, OnDestroy {

  public isSidepanelOpen: boolean;
  public topoData: {nodes: NodeData[], links: EdgeData[], type: TopologyType};
  public domain: ContivNodeDataModel;
  public bdId: string;
  public showedTables: boolean[];
  public tableType: string;
  public svgTransform: SvgTransform;

  private subscriptions: Subscription[];

  constructor(
    private router: Router,
    private route: ActivatedRoute,
    private bdService: BridgeDomainService,
    private dataService: DataService,
    private topologyService: TopologyService,
    private sidepanelService: SidepanelService,
    private topologyHighlightService: TopologyHighlightService,
    private layoutService: LayoutService
  ) { }

  ngOnInit() {
    this.svgTransform = {
      translate: [-180, 0],
      scale: [1]
    };

    this.init();
    this.subscriptions.push(this.dataService.isContivDataLoaded.subscribe(dataLoaded => {
      if (dataLoaded) {
        this.topoData = this.bdService.getTopologyData(this.dataService.contivData);
        this.topoData.type = 'bd';
        this.domain = this.dataService.contivData.contivData[0];
        this.bdId = this.domain.bd[0].name;

        const topo: TopologyDataModel = new TopologyDataModel();
        topo.setData(this.topoData.nodes, this.topoData.links);
        this.topologyService.setTopologyData(topo);
      }
    }));
  }

  public toggleSidepanel(state: boolean) {
    this.isSidepanelOpen = state;
  }

  public onNodeClicked(data: NodeClickEvent) {
    this.topologyHighlightService.clearSelections();
    this.sidepanelService.setSpNodeItem(data.node, this.topoData.type);

    this.resetTables();
  }

  public onNodeDblClicked(data: NodeClickEvent) {
    this.router.navigate(['vswitch-diagram', data.node.id]);

    this.resetTables();
  }

  public onBviClicked(data: NodeClickEvent) {
    this.topologyHighlightService.clearSelections();
    this.sidepanelService.setSpNodeItem(data.node, this.topoData.type);
    this.topologyHighlightService.highlightBVI(data.node.id);
    this.topologyHighlightService.highlightTunnelFromToNode(data.node.id);

    this.resetTables();
  }

  public onLinkClicked(data: LinkClickEvent) {
    this.topologyHighlightService.clearSelections();

    let linkType: 'vppLink' | 'vxtunnel';
    if (this.isVppLink(data.link)) {
      linkType = 'vppLink';
    } else if (this.isVxtunnelLink(data.link)) {
      linkType = 'vxtunnel';
    }

    this.resetTables();

    this.sidepanelService.setSpLinkItem(data.link, linkType, this.topoData.type);
  }

  public onSvgClicked() {
    this.topologyHighlightService.clearSelections();
    this.sidepanelService.setSpNodeItem(null, this.topoData.type);
  }

  public onTransform(transform: SvgTransform) {
  }

  public onRender() {
    this.topologyHighlightService.setLayer('vpp-3', this.topoData.type);
  }

  public onPositionChange(topologyData: TopologyDataModel) {
    this.layoutService.saveNodesPositions('bd-topo', topologyData);
  }

  public showSummary() {
    this.onSvgClicked();

    if (this.showedTables[0]) {
      this.resetTables();
    } else {
      this.showedTables = [true, false, false];

      if (this.tableType === '') {
        this.tableType = 'summary';
      } else {
        this.tableType = '';
        setTimeout(() => this.tableType = 'summary', 300);
      }
    }
  }

  public showPods() {
    this.onSvgClicked();

    if (this.showedTables[1]) {
      this.resetTables();
    } else {
      this.showedTables = [false, true, false];

      if (this.tableType === '') {
        this.tableType = 'pods';
      } else {
        this.tableType = '';
        setTimeout(() => this.tableType = 'pods', 300);
      }
    }
  }

  public showTunnels() {
    this.onSvgClicked();

    if (this.showedTables[2]) {
      this.resetTables();
    } else {
      this.showedTables = [false, false, true];

      if (this.tableType === '') {
        this.tableType = 'tunnels';
      } else {
        this.tableType = '';
        setTimeout(() => this.tableType = 'tunnels', 300);
      }
    }
  }

  public showDetail(data: {nodeId: string, type: 'vppPod' | 'bvi' | 'vxtunnel', dstId: string}) {
    this.resetTables();
    this.isSidepanelOpen = true;

    switch (data.type) {
      case 'vppPod':
        this.topologyHighlightService.highlightNode(data.nodeId);
        this.router.navigate([data.type, data.nodeId], {relativeTo: this.route});
        break;
      case 'bvi':
        this.topologyHighlightService.highlightBVI(data.nodeId);
        this.topologyHighlightService.highlightTunnelFromToNode(data.nodeId);
        this.router.navigate([data.type, data.nodeId], {relativeTo: this.route});
        break;
      case 'vxtunnel':
        this.topologyHighlightService.highlightLinkBetweenNodes(data.nodeId, data.dstId);
        this.router.navigate([data.type, data.nodeId, data.dstId], {relativeTo: this.route});
        break;
    }
  }

  public resetTables() {
    this.showedTables = [false, false, false];
    this.tableType = '';
  }

  private isVppLink(link: EdgeDataModel): boolean {
    return link.fromNodeObj instanceof VppTopoPod && link.toNodeObj instanceof VppTopoBvi ||
      link.fromNodeObj instanceof VppTopoBvi && link.toNodeObj instanceof VppTopoPod;
  }

  private isVxtunnelLink(link: EdgeDataModel): boolean {
    return link.fromNodeObj instanceof VppTopoBvi && link.toNodeObj instanceof VppTopoBvi;
  }

  private init() {
    this.subscriptions = [];
    this.topoData = {nodes: [], links: [], type: 'k8s'};
    this.isSidepanelOpen = false;
    this.showedTables = [false, false, false];
    this.tableType = '';
  }

  ngOnDestroy() {
    this.subscriptions.forEach(s => s.unsubscribe);
  }
}


ngOnInit() {
    this.svgTransform = {
      translate: [-180, 0],
      scale: [1]
    };

    this.init();
    this.subscriptions.push(this.dataService.isContivDataLoaded.subscribe(dataLoaded => {
      if (dataLoaded) {
        this.topoData = this.bdService.getTopologyData(this.dataService.contivData);
        this.topoData.type = 'bd';
        this.domain = this.dataService.contivData.contivData[0];
        this.bdId = this.domain.bd[0].name;

        const topo: TopologyDataModel = new TopologyDataModel();
        topo.setData(this.topoData.nodes, this.topoData.links);
        this.topologyService.setTopologyData(topo);
      }
    }));
  }


public toggleSidepanel(state: boolean) {
    this.isSidepanelOpen = state;
  }


public onNodeClicked(data: NodeClickEvent) {
    this.topologyHighlightService.clearSelections();
    this.sidepanelService.setSpNodeItem(data.node, this.topoData.type);

    this.resetTables();
  }


public onNodeDblClicked(data: NodeClickEvent) {
    this.router.navigate(['vswitch-diagram', data.node.id]);

    this.resetTables();
  }


public onBviClicked(data: NodeClickEvent) {
    this.topologyHighlightService.clearSelections();
    this.sidepanelService.setSpNodeItem(data.node, this.topoData.type);
    this.topologyHighlightService.highlightBVI(data.node.id);
    this.topologyHighlightService.highlightTunnelFromToNode(data.node.id);

    this.resetTables();
  }


public onLinkClicked(data: LinkClickEvent) {
    this.topologyHighlightService.clearSelections();

    let linkType: 'vppLink' | 'vxtunnel';
    if (this.isVppLink(data.link)) {
      linkType = 'vppLink';
    } else if (this.isVxtunnelLink(data.link)) {
      linkType = 'vxtunnel';
    }

    this.resetTables();

    this.sidepanelService.setSpLinkItem(data.link, linkType, this.topoData.type);
  }


public onSvgClicked() {
    this.topologyHighlightService.clearSelections();
    this.sidepanelService.setSpNodeItem(null, this.topoData.type);
  }


public onTransform(transform: SvgTransform) {
  }


public onRender() {
    this.topologyHighlightService.setLayer('vpp-3', this.topoData.type);
  }


public onPositionChange(topologyData: TopologyDataModel) {
    this.layoutService.saveNodesPositions('bd-topo', topologyData);
  }


public showSummary() {
    this.onSvgClicked();

    if (this.showedTables[0]) {
      this.resetTables();
    } else {
      this.showedTables = [true, false, false];

      if (this.tableType === '') {
        this.tableType = 'summary';
      } else {
        this.tableType = '';
        setTimeout(() => this.tableType = 'summary', 300);
      }
    }
  }


public showPods() {
    this.onSvgClicked();

    if (this.showedTables[1]) {
      this.resetTables();
    } else {
      this.showedTables = [false, true, false];

      if (this.tableType === '') {
        this.tableType = 'pods';
      } else {
        this.tableType = '';
        setTimeout(() => this.tableType = 'pods', 300);
      }
    }
  }


public showTunnels() {
    this.onSvgClicked();

    if (this.showedTables[2]) {
      this.resetTables();
    } else {
      this.showedTables = [false, false, true];

      if (this.tableType === '') {
        this.tableType = 'tunnels';
      } else {
        this.tableType = '';
        setTimeout(() => this.tableType = 'tunnels', 300);
      }
    }
  }


public showDetail(data: {nodeId: string, type: 'vppPod' | 'bvi' | 'vxtunnel', dstId: string}) {
    this.resetTables();
    this.isSidepanelOpen = true;

    switch (data.type) {
      case 'vppPod':
        this.topologyHighlightService.highlightNode(data.nodeId);
        this.router.navigate([data.type, data.nodeId], {relativeTo: this.route});
        break;
      case 'bvi':
        this.topologyHighlightService.highlightBVI(data.nodeId);
        this.topologyHighlightService.highlightTunnelFromToNode(data.nodeId);
        this.router.navigate([data.type, data.nodeId], {relativeTo: this.route});
        break;
      case 'vxtunnel':
        this.topologyHighlightService.highlightLinkBetweenNodes(data.nodeId, data.dstId);
        this.router.navigate([data.type, data.nodeId, data.dstId], {relativeTo: this.route});
        break;
    }
  }


public resetTables() {
    this.showedTables = [false, false, false];
    this.tableType = '';
  }


private isVppLink(link: EdgeDataModel): boolean {
    return link.fromNodeObj instanceof VppTopoPod && link.toNodeObj instanceof VppTopoBvi ||
      link.fromNodeObj instanceof VppTopoBvi && link.toNodeObj instanceof VppTopoPod;
  }


private isVxtunnelLink(link: EdgeDataModel): boolean {
    return link.fromNodeObj instanceof VppTopoBvi && link.toNodeObj instanceof VppTopoBvi;
  }


private init() {
    this.subscriptions = [];
    this.topoData = {nodes: [], links: [], type: 'k8s'};
    this.isSidepanelOpen = false;
    this.showedTables = [false, false, false];
    this.tableType = '';
  }


ngOnDestroy() {
    this.subscriptions.forEach(s => s.unsubscribe);
  }


@Injectable()
export class ProfileDeleteService extends GenericService<Profile>{
    constructor(
        @InjectRepository(ProfileDeleteRepository)
        private readonly repository : ProfileDeleteRepository){
        super();
    }
    execute(param: IQueryParamByID<number>): Promise<void> {
        console.log("Delete Service");
        return this.repository.execute(param);
    }

}


execute(param: IQueryParamByID<number>): Promise<void> {
        console.log("Delete Service");
        return this.repository.execute(param);
    }


class Storage implements IStorage {
    notes: Note[] = []
    tags: Tag[] = []
    users: User[] = []
    constructor(data?: Storage) {
        if (data) {
            this.notes = data.notes
            this.tags = data.tags
            this.users = data.users
        }

    }

    public async readStorage(): Promise<string> {
        try {
            return await fs.promises.readFile('storeFile.txt', 'utf-8');
        } catch (err) {
            return ""
        }
    }
    public async updateStorage(dataToSave: string): Promise<void> {
        try {
            await fs.promises.writeFile('storeFile.txt', dataToSave);
        } catch (err) {
            return
        }
    }
}


public async readStorage(): Promise<string> {
        try {
            return await fs.promises.readFile('storeFile.txt', 'utf-8');
        } catch (err) {
            return ""
        }
    }


public async updateStorage(dataToSave: string): Promise<void> {
        try {
            await fs.promises.writeFile('storeFile.txt', dataToSave);
        } catch (err) {
            return
        }
    }


api => next => action => {
  if (action.type === Consts.storage) {
    switch (action.storageType) {
      case StorageType.Add:
        //save item

        break;
      case StorageType.Remove:
        //remove item

        break;
      default:
        break;
    }
  } else {
    return next(action);
  }
}


next => action => {
  if (action.type === Consts.storage) {
    switch (action.storageType) {
      case StorageType.Add:
        //save item

        break;
      case StorageType.Remove:
        //remove item

        break;
      default:
        break;
    }
  } else {
    return next(action);
  }
}


action => {
  if (action.type === Consts.storage) {
    switch (action.storageType) {
      case StorageType.Add:
        //save item

        break;
      case StorageType.Remove:
        //remove item

        break;
      default:
        break;
    }
  } else {
    return next(action);
  }
}


function isVueVar(name: string): boolean {
  return /^:/.test(name);
}


function isVueEvent(name: string): boolean {
  return /^@/.test(name);
}


function isObjectOrArrayStr(name: string): boolean {
  return /^(\{|\[)/.test(name);
}


function isArrayStr(name: string): boolean {
  return /^\[[\s\S]+\]$/.test(name);
}


function isObject(o: any): boolean {
  return Object.prototype.toString.call(o) === '[object Object]';
}


/**
 *
 "[ 'ass', { 'xxx': isLink } ]" => [ 'ass', { 'xxx': 'isLink' } ]
 * "ass {{ isLink ? 'xxx' : '' }}"
 */
function handleArrayStr(source: string): string {
  source = source.replace(/[\s:]([\w\.]+)[\s:]/g, ' \'$1\'');
  const arr = new Function(`return ${source}`)();

  let result = '';
  arr.forEach((item: any) => {
    if (typeof item === 'string') {
      result += item + ' ';
    }

    if (isObject(item)) {
      Object.keys(item).forEach(key => {
        result += `{{ ${item[key]} ? '${key}' : '' }}`;
      })
    }
  })

  return result;
}


export function wrapVar(v: string): string {
  return `{{ ${v} }}`
}


(tag) => {
  let weappTag = '';

  if (tag === 'img') {
    weappTag = 'image';
  } else if (tag === 'template') {
    weappTag = 'block';
  } else if (block[tag] || closeSelf[tag] || inline[tag]) {
    weappTag = 'view';
  }

  return weappTag || tag;
}


(item: any) => {
    if (typeof item === 'string') {
      result += item + ' ';
    }

    if (isObject(item)) {
      Object.keys(item).forEach(key => {
        result += `{{ ${item[key]} ? '${key}' : '' }}`;
      })
    }
  }


key => {
        result += `{{ ${item[key]} ? '${key}' : '' }}`;
      }


({
  name,
  value,
  ...rest
}) => {
  let needsWrap = false;
  let extraStr = '';
  const replacement = replaceMap[name]

  if (replacement) {
    name = replacement.name;
    needsWrap = replacement.needsWrap;
  }

  if (name === 'v-for') {
    name = 'wx:for'
    // 解析成功的话就用解析的，否则返回原始值
    const [, forItem, traverseVar] = value.match(/([^\s]+)\s+in\s+([^\s]+)/) || ['' , '', value];

    if (forItem) {
      extraStr = `wx:for-item="${forItem}"`;
    }

    value = traverseVar;
    needsWrap = true;
  }

  if (isVueVar(name)) {
    name = name.replace(':', '');
    needsWrap = !isObjectOrArrayStr(value);

    // TODO: 可能会遇到的对象字符串
    // if (isObjectStr(value)) { }

    if (isArrayStr(value)) {
      value = handleArrayStr(value);
    }
  }

  if (isVueEvent(name)) {
    let [, eventName] = name.match(/^@([^@]+)/) || [ , ''];

    if (eventName === 'click') {
      eventName = 'tap';
    }

    name = `bind:${eventName}`;
  }

  return {
    ...rest,
    extraStr,
    name,
    value: needsWrap ? wrapVar(value) : value
  };
}


export default function version(
  {
    push,
    remote,
    dryRun,
    baseBranch,
    noVerify,
    syncVersions,
    skipRootChangelog,
    skipProjectChangelog,
    version,
    preid,
    changelogHeader,
  }: VersionBuilderSchema,
  context: ExecutorContext
): Promise<{ success: boolean }> {
  const workspaceRoot = context.root;
  const preset = 'angular';
  const tagPrefix = syncVersions ? 'v' : `${context.projectName}-`;

  const projectRoot = getProjectRoot(context);
  const newVersion$ = tryBump({
    preset,
    projectRoot,
    tagPrefix,
    releaseType: version,
    preid,
  });

  const action$ = newVersion$.pipe(
    switchMap((newVersion) => {
      if (newVersion == null) {
        logger.info('⏹ Nothing changed since last release.');
        return of(undefined);
      }

      const options: CommonVersionOptions = {
        dryRun,
        newVersion,
        noVerify,
        preset,
        projectRoot,
        tagPrefix,
        changelogHeader,
      };

      const runStandardVersion$ = defer(() =>
        syncVersions
          ? versionWorkspace({
              ...options,
              skipRootChangelog,
              skipProjectChangelog,
              workspaceRoot,
            })
          : versionProject(options)
      );
      const pushToGitRemote$ = defer(() =>
        tryPushToGitRemote({
          branch: baseBranch,
          noVerify,
          remote,
        })
      );

      return concat(
        runStandardVersion$,
        ...(push && dryRun === false ? [pushToGitRemote$] : [])
      );
    })
  );

  return action$.pipe(
    mapTo({ success: true }),
    catchError((error) => {
      logger.error(error.stack ?? error.toString());
      return of({ success: false });
    })
  ).toPromise();
}


(newVersion) => {
      if (newVersion == null) {
        logger.info('⏹ Nothing changed since last release.');
        return of(undefined);
      }

      const options: CommonVersionOptions = {
        dryRun,
        newVersion,
        noVerify,
        preset,
        projectRoot,
        tagPrefix,
        changelogHeader,
      };

      const runStandardVersion$ = defer(() =>
        syncVersions
          ? versionWorkspace({
              ...options,
              skipRootChangelog,
              skipProjectChangelog,
              workspaceRoot,
            })
          : versionProject(options)
      );
      const pushToGitRemote$ = defer(() =>
        tryPushToGitRemote({
          branch: baseBranch,
          noVerify,
          remote,
        })
      );

      return concat(
        runStandardVersion$,
        ...(push && dryRun === false ? [pushToGitRemote$] : [])
      );
    }


() =>
        syncVersions
          ? versionWorkspace({
              ...options,
              skipRootChangelog,
              skipProjectChangelog,
              workspaceRoot,
            })
          : versionProject(options)


() =>
        tryPushToGitRemote({
          branch: baseBranch,
          noVerify,
          remote,
        })


(error) => {
      logger.error(error.stack ?? error.toString());
      return of({ success: false });
    }


() => {
  let component: ToastComponent;
  let fixture: ComponentFixture<ToastComponent>;

  // tslint:disable-next-line
  @Component({selector: 'ngb-toast', template: ''})
  class NgbToastComponent {
    @Input() header;
    @Input() autohide;
    @Input() delay;
    @Input() class;
  }

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ToastComponent, NgbToastComponent ],
      providers: [
        { provide: ToastService, use: {} },
      ]
    })
    .compileComponents()
    .then(() => {
      fixture = TestBed.createComponent(ToastComponent);
      component = fixture.componentInstance;
    });
  }));

  beforeEach(() => {
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
}


() => {
    TestBed.configureTestingModule({
      declarations: [ ToastComponent, NgbToastComponent ],
      providers: [
        { provide: ToastService, use: {} },
      ]
    })
    .compileComponents()
    .then(() => {
      fixture = TestBed.createComponent(ToastComponent);
      component = fixture.componentInstance;
    });
  }


() => {
      fixture = TestBed.createComponent(ToastComponent);
      component = fixture.componentInstance;
    }


// tslint:disable-next-line
@Component({selector: 'ngb-toast', template: ''})
  class NgbToastComponent {
    @Input() header;
    @Input() autohide;
    @Input() delay;
    @Input() class;
  }


() => {
    const error = new Error();
    const rejected = XPromise.reject(error);
    const value = Symbol();
    const resolved = XPromise.resolve(value);

    const bar1 = new XPromise((resolve) => resolve(rejected));
    void expect(bar1).rejects.toBe(error);

    const bar2 = new XPromise((resolve, reject) => reject(rejected));
    void expect(bar2).rejects.toBe(rejected);

    const bar3 = new XPromise((resolve) => resolve(resolved));
    void expect(bar3).resolves.toBe(value);

    const bar4 = new XPromise((resolve, reject) => reject(resolved));
    void expect(bar4).rejects.toBe(resolved);
}


(resolve) => resolve(rejected)


(resolve, reject) => reject(rejected)


(resolve) => resolve(resolved)


(resolve, reject) => reject(resolved)


() => {
    const err = {};
    const promise = new XPromise(() => { throw err; });
    expect(promise.isRejected()).toBe(true);
    void expect(promise).rejects.toBe(err);
}


() => { throw err; }


/**
 * @title Basic list
 */
@Component({
  selector: 'list-overview-example',
  templateUrl: 'list-overview-example.html',
})
export class ListOverviewExample {}


() => this.getUserDataEditorInputSource(editorService.activeEditor)


e => e.id === VIEWLET_ID && e.visible


source => source !== undefined


() => 'windowFocus'


source => source!


(last, source) => last ? [...last, source] : [source]


sources => userDataAutoSyncService.triggerSync(sources, true, false)


source => userDataAutoSyncService.triggerSync([source!], true, false)


export class UserDataSyncTrigger extends Disposable implements IWorkbenchContribution {

	constructor(
		@IEditorService editorService: IEditorService,
		@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
		@IViewsService viewsService: IViewsService,
		@IUserDataAutoSyncService userDataAutoSyncService: IUserDataAutoSyncService,
		@IHostService hostService: IHostService,
	) {
		super();
		const event = Event.filter(
			Event.any<string | undefined>(
				Event.map(editorService.onDidActiveEditorChange, () => this.getUserDataEditorInputSource(editorService.activeEditor)),
				Event.map(Event.filter(viewsService.onDidChangeViewContainerVisibility, e => e.id === VIEWLET_ID && e.visible), e => e.id)
			), source => source !== undefined);
		if (isWeb) {
			this._register(Event.debounce<string, string[]>(
				Event.any<string>(
					Event.map(hostService.onDidChangeFocus, () => 'windowFocus'),
					Event.map(event, source => source!),
				), (last, source) => last ? [...last, source] : [source], 1000)
				(sources => userDataAutoSyncService.triggerSync(sources, true, false)));
		} else {
			this._register(event(source => userDataAutoSyncService.triggerSync([source!], true, false)));
		}
	}

	private getUserDataEditorInputSource(editorInput: IEditorInput | undefined): string | undefined {
		if (!editorInput) {
			return undefined;
		}
		if (editorInput instanceof SettingsEditor2Input) {
			return 'settingsEditor';
		}
		if (editorInput instanceof PreferencesEditorInput) {
			return 'settingsEditor';
		}
		if (editorInput instanceof KeybindingsEditorInput) {
			return 'keybindingsEditor';
		}
		const resource = editorInput.resource;
		if (isEqual(resource, this.environmentService.settingsResource)) {
			return 'settingsEditor';
		}
		if (isEqual(resource, this.environmentService.keybindingsResource)) {
			return 'keybindingsEditor';
		}
		return undefined;
	}
}


private getUserDataEditorInputSource(editorInput: IEditorInput | undefined): string | undefined {
		if (!editorInput) {
			return undefined;
		}
		if (editorInput instanceof SettingsEditor2Input) {
			return 'settingsEditor';
		}
		if (editorInput instanceof PreferencesEditorInput) {
			return 'settingsEditor';
		}
		if (editorInput instanceof KeybindingsEditorInput) {
			return 'keybindingsEditor';
		}
		const resource = editorInput.resource;
		if (isEqual(resource, this.environmentService.settingsResource)) {
			return 'settingsEditor';
		}
		if (isEqual(resource, this.environmentService.keybindingsResource)) {
			return 'keybindingsEditor';
		}
		return undefined;
	}


export class DeviceIdentity {
  constructor(bytes:number[]){
    this.firmwareVersion = bytes[12]+'.'+bytes[13];
    // Parse serial number
    this.serialNumber = bit7array2dec(bytes.slice(16,21));
    // parse build number
    this.softwareBuild = bit7array2dec(bytes.slice(14,16));
    // device family code
    this.deviceFamilyCode = bit7array2dec(bytes.slice(8,10));
    // device family member code
    this.deviceFamilyMemberCode = bit7array2dec(bytes.slice(10,12));
    this.boardRevision = bytes[21];
  }
}


export interface DeviceIdentity{
  firmwareVersion:string;
  serialNumber:number;
  softwareBuild:number;
  deviceFamilyCode:number;
  deviceFamilyMemberCode:number;
  boardRevision:number;
}


() => (
    <>
      <TagField
        label="Tags (optional)"


() => {
  const { NATS } = getEnvVar({ NATS: 'localhost' })
  return connect({
    servers: `nats://${NATS}:4222`,
    reconnect: true,
    waitOnFirstConnect: true,
    maxReconnectAttempts: -1 // Retry forever
  })
}


async (
  topics: SingleOrArray<string>,
  processor: NatsProcessorFn,
  options: SubscriptionOptions = {}
) => {
  const natsClient = await initializeNatsClient()
  try {
    await Promise.all(
      asArray(topics).map(topic =>
        natsClient.subscribe(topic, {
          ...options,
          callback: natsCbWrapper(processor)
        })
      )
    )
  } catch (err) {
    StreamLogger.error(err)
  }
  return natsClient
}


topic =>
        natsClient.subscribe(topic, {
          ...options,
          callback: natsCbWrapper(processor)
        })


async () => {
  return initializeNatsClient()
}


(consumer: NatsConnection) => consumer.close()


/**
 * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a
 * "type check block" function.
 *
 * When passed through TypeScript's TypeChecker, type errors that arise within the type check block
 * function indicate issues in the template itself.
 *
 * As a side effect of generating a TCB for the component, `ts.Diagnostic`s may also be produced
 * directly for issues within the template which are identified during generation. These issues are
 * recorded in either the `domSchemaChecker` (which checks usage of DOM elements and bindings) as
 * well as the `oobRecorder` (which records errors when the type-checking code generator is unable
 * to sufficiently understand a template).
 *
 * @param env an `Environment` into which type-checking code will be generated.
 * @param ref a `Reference` to the component class which should be type-checked.
 * @param name a `ts.Identifier` to use for the generated `ts.FunctionDeclaration`.
 * @param meta metadata about the component's template and the function being generated.
 * @param domSchemaChecker used to check and record errors regarding improper usage of DOM elements
 * and bindings.
 * @param oobRecorder used to record errors regarding template elements which could not be correctly
 * translated into types during TCB generation.
 */
export function generateTypeCheckBlock(
    env: Environment, ref: Reference<ClassDeclaration<ts.ClassDeclaration>>, name: ts.Identifier,
    meta: TypeCheckBlockMetadata, domSchemaChecker: DomSchemaChecker,
    oobRecorder: OutOfBandDiagnosticRecorder): ts.FunctionDeclaration {
  const tcb = new Context(
      env, domSchemaChecker, oobRecorder, meta.id, meta.boundTarget, meta.pipes, meta.schemas);
  const scope = Scope.forNodes(tcb, null, tcb.boundTarget.target.template !, /* guard */ null);
  const ctxRawType = env.referenceType(ref);
  if (!ts.isTypeReferenceNode(ctxRawType)) {
    throw new Error(
        `Expected TypeReferenceNode when referencing the ctx param for ${ref.debugName}`);
  }
  const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, env.config.useContextGenericType)];

  const scopeStatements = scope.render();
  const innerBody = ts.createBlock([
    ...env.getPreludeStatements(),
    ...scopeStatements,
  ]);

  // Wrap the body in an "if (true)" expression. This is unnecessary but has the effect of causing
  // the `ts.Printer` to format the type-check block nicely.
  const body = ts.createBlock([ts.createIf(ts.createTrue(), innerBody, undefined)]);
  const fnDecl = ts.createFunctionDeclaration(
      /* decorators */ undefined,
      /* modifiers */ undefined,
      /* asteriskToken */ undefined,
      /* name */ name,
      /* typeParameters */ env.config.useContextGenericType ? ref.node.typeParameters : undefined,
      /* parameters */ paramList,
      /* type */ undefined,
      /* body */ body);
  addTemplateId(fnDecl, meta.id);
  return fnDecl;
}


/**
 * Create the `ctx` parameter to the top-level TCB function.
 *
 * This is a parameter with a type equivalent to the component type, with all generic type
 * parameters listed (without their generic bounds).
 */
function tcbCtxParam(
    node: ClassDeclaration<ts.ClassDeclaration>, name: ts.EntityName,
    useGenericType: boolean): ts.ParameterDeclaration {
  let typeArguments: ts.TypeNode[]|undefined = undefined;
  // Check if the component is generic, and pass generic type parameters if so.
  if (node.typeParameters !== undefined) {
    if (useGenericType) {
      typeArguments =
          node.typeParameters.map(param => ts.createTypeReferenceNode(param.name, undefined));
    } else {
      typeArguments =
          node.typeParameters.map(() => ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword));
    }
  }
  const type = ts.createTypeReferenceNode(name, typeArguments);
  return ts.createParameter(
      /* decorators */ undefined,
      /* modifiers */ undefined,
      /* dotDotDotToken */ undefined,
      /* name */ 'ctx',
      /* questionToken */ undefined,
      /* type */ type,
      /* initializer */ undefined);
}


/**
 * Process an `AST` expression and convert it into a `ts.Expression`, generating references to the
 * correct identifiers in the current scope.
 */
function tcbExpression(ast: AST, tcb: Context, scope: Scope): ts.Expression {
  const translator = new TcbExpressionTranslator(tcb, scope);
  return translator.translate(ast);
}


/**
 * Call the type constructor of a directive instance on a given template node, inferring a type for
 * the directive instance from any bound inputs.
 */
function tcbCallTypeCtor(
    dir: TypeCheckableDirectiveMeta, tcb: Context, inputs: TcbDirectiveInput[]): ts.Expression {
  const typeCtor = tcb.env.typeCtorFor(dir);

  // Construct an array of `ts.PropertyAssignment`s for each of the directive's inputs.
  const members = inputs.map(input => {
    const propertyName = ts.createStringLiteral(input.field);

    if (input.type === 'binding') {
      // For bound inputs, the property is assigned the binding expression.
      let expr = input.expression;
      if (!tcb.env.config.checkTypeOfInputBindings) {
        // If checking the type of bindings is disabled, cast the resulting expression to 'any'
        // before the assignment.
        expr = tsCastToAny(expr);
      } else if (!tcb.env.config.strictNullInputBindings) {
        // If strict null checks are disabled, erase `null` and `undefined` from the type by
        // wrapping the expression in a non-null assertion.
        expr = ts.createNonNullExpression(expr);
      }

      const assignment = ts.createPropertyAssignment(propertyName, wrapForDiagnostics(expr));
      addParseSpanInfo(assignment, input.sourceSpan);
      return assignment;
    } else {
      // A type constructor is required to be called with all input properties, so any unset
      // inputs are simply assigned a value of type `any` to ignore them.
      return ts.createPropertyAssignment(propertyName, NULL_AS_ANY);
    }
  });

  // Call the `ngTypeCtor` method on the directive class, with an object literal argument created
  // from the matched inputs.
  return ts.createCall(
      /* expression */ typeCtor,
      /* typeArguments */ undefined,
      /* argumentsArray */[ts.createObjectLiteral(members)]);
}


function tcbGetDirectiveInputs(
    el: TmplAstElement|TmplAstTemplate, dir: TypeCheckableDirectiveMeta, tcb: Context,
    scope: Scope): TcbDirectiveInput[] {
  // Only the first binding to a property is written.
  // TODO(alxhub): produce an error for duplicate bindings to the same property, independently of
  // this logic.
  const directiveInputs = new Map<string, TcbDirectiveInput>();
  // `dir.inputs` is an object map of field names on the directive class to property names.
  // This is backwards from what's needed to match bindings - a map of properties to field names
  // is desired. Invert `dir.inputs` into `propMatch` to create this map.
  const propMatch = new Map<string, string>();
  const inputs = dir.inputs;
  Object.keys(inputs).forEach(key => {
    Array.isArray(inputs[key]) ? propMatch.set(inputs[key][0], key) :
                                 propMatch.set(inputs[key] as string, key);
  });

  el.inputs.forEach(processAttribute);
  el.attributes.forEach(processAttribute);
  if (el instanceof TmplAstTemplate) {
    el.templateAttrs.forEach(processAttribute);
  }

  // Add unset directive inputs for each of the remaining unset fields.
  // Note: it's actually important here that `propMatch.values()` isn't used, as there can be
  // multiple fields which share the same property name and only one of them will be listed as a
  // value in `propMatch`.
  for (const field of Object.keys(inputs)) {
    if (!directiveInputs.has(field)) {
      directiveInputs.set(field, {type: 'unset', field});
    }
  }

  return Array.from(directiveInputs.values());

  /**
   * Add a binding expression to the map for each input/template attribute of the directive that has
   * a matching binding.
   */
  function processAttribute(attr: TmplAstBoundAttribute|TmplAstTextAttribute): void {
    // Skip non-property bindings.
    if (attr instanceof TmplAstBoundAttribute && attr.type !== BindingType.Property) {
      return;
    }

    // Skip text attributes if configured to do so.
    if (!tcb.env.config.checkTypeOfAttributes && attr instanceof TmplAstTextAttribute) {
      return;
    }

    // Skip the attribute if the directive does not have an input for it.
    if (!propMatch.has(attr.name)) {
      return;
    }
    const field = propMatch.get(attr.name)!;

    // Skip the attribute if a previous binding also wrote to it.
    if (directiveInputs.has(field)) {
      return;
    }

    let expr: ts.Expression;
    if (attr instanceof TmplAstBoundAttribute) {
      // Produce an expression representing the value of the binding.
      expr = tcbExpression(attr.value, tcb, scope);
    } else {
      // For regular attributes with a static string value, use the represented string literal.
      expr = ts.createStringLiteral(attr.value);
    }

    directiveInputs.set(field, {
      type: 'binding',
      field: field,
      expression: expr,
      sourceSpan: attr.sourceSpan,
    });
  }
}


/**
   * Add a binding expression to the map for each input/template attribute of the directive that has
   * a matching binding.
   */
function processAttribute(attr: TmplAstBoundAttribute|TmplAstTextAttribute): void {
    // Skip non-property bindings.
    if (attr instanceof TmplAstBoundAttribute && attr.type !== BindingType.Property) {
      return;
    }

    // Skip text attributes if configured to do so.
    if (!tcb.env.config.checkTypeOfAttributes && attr instanceof TmplAstTextAttribute) {
      return;
    }

    // Skip the attribute if the directive does not have an input for it.
    if (!propMatch.has(attr.name)) {
      return;
    }
    const field = propMatch.get(attr.name)!;

    // Skip the attribute if a previous binding also wrote to it.
    if (directiveInputs.has(field)) {
      return;
    }

    let expr: ts.Expression;
    if (attr instanceof TmplAstBoundAttribute) {
      // Produce an expression representing the value of the binding.
      expr = tcbExpression(attr.value, tcb, scope);
    } else {
      // For regular attributes with a static string value, use the represented string literal.
      expr = ts.createStringLiteral(attr.value);
    }

    directiveInputs.set(field, {
      type: 'binding',
      field: field,
      expression: expr,
      sourceSpan: attr.sourceSpan,
    });
  }


/**
 * Creates an arrow function to be used as handler function for event bindings. The handler
 * function has a single parameter `$event` and the bound event's handler `AST` represented as a
 * TypeScript expression as its body.
 *
 * When `eventType` is set to `Infer`, the `$event` parameter will not have an explicit type. This
 * allows for the created handler function to have its `$event` parameter's type inferred based on
 * how it's used, to enable strict type checking of event bindings. When set to `Any`, the `$event`
 * parameter will have an explicit `any` type, effectively disabling strict type checking of event
 * bindings. Alternatively, an explicit type can be passed for the `$event` parameter.
 */
function tcbCreateEventHandler(
    event: TmplAstBoundEvent, tcb: Context, scope: Scope,
    eventType: EventParamType|ts.TypeNode): ts.Expression {
  const handler = tcbEventHandlerExpression(event.handler, tcb, scope);

  let eventParamType: ts.TypeNode|undefined;
  if (eventType === EventParamType.Infer) {
    eventParamType = undefined;
  } else if (eventType === EventParamType.Any) {
    eventParamType = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
  } else {
    eventParamType = eventType;
  }

  // Obtain all guards that have been applied to the scope and its parents, as they have to be
  // repeated within the handler function for their narrowing to be in effect within the handler.
  const guards = scope.guards();

  let body: ts.Statement = ts.createExpressionStatement(handler);
  if (guards !== null) {
    // Wrap the body in an `if` statement containing all guards that have to be applied.
    body = ts.createIf(guards, body);
  }

  const eventParam = ts.createParameter(
      /* decorators */ undefined,
      /* modifiers */ undefined,
      /* dotDotDotToken */ undefined,
      /* name */ EVENT_PARAMETER,
      /* questionToken */ undefined,
      /* type */ eventParamType);

  return ts.createFunctionExpression(
      /* modifier */ undefined,
      /* asteriskToken */ undefined,
      /* name */ undefined,
      /* typeParameters */ undefined,
      /* parameters */[eventParam],
      /* type */ ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
      /* body */ ts.createBlock([body]));
}


/**
 * Similar to `tcbExpression`, this function converts the provided `AST` expression into a
 * `ts.Expression`, with special handling of the `$event` variable that can be used within event
 * bindings.
 */
function tcbEventHandlerExpression(ast: AST, tcb: Context, scope: Scope): ts.Expression {
  const translator = new TcbEventHandlerTranslator(tcb, scope);
  return translator.translate(ast);
}


export function requiresInlineTypeCheckBlock(node: ClassDeclaration<ts.ClassDeclaration>): boolean {
  // In order to qualify for a declared TCB (not inline) two conditions must be met:
  // 1) the class must be exported
  // 2) it must not have constrained generic types
  if (!checkIfClassIsExported(node)) {
    // Condition 1 is false, the class is not exported.
    return true;
  } else if (!checkIfGenericTypesAreUnbound(node)) {
    // Condition 2 is false, the class has constrained generic types
    return true;
  } else {
    return false;
  }
}


guard => {
          // For each template guard function on the directive, look for a binding to that input.
          const boundInput = this.template.inputs.find(i => i.name === guard.inputName) ||
              this.template.templateAttrs.find(
                  (i: TmplAstTextAttribute|TmplAstBoundAttribute): i is TmplAstBoundAttribute =>
                      i instanceof TmplAstBoundAttribute && i.name === guard.inputName);
          if (boundInput !== undefined) {
            // If there is such a binding, generate an expression for it.
            const expr = tcbExpression(boundInput.value, this.tcb, this.scope);

            // The expression has already been checked in the type constructor invocation, so
            // it should be ignored when used within a template guard.
            ignoreDiagnostics(expr);

            if (guard.type === 'binding') {
              // Use the binding expression itself as guard.
              directiveGuards.push(expr);
            } else {
              // Call the guard function on the directive with the directive instance and that
              // expression.
              const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
                dirInstId,
                expr,
              ]);
              addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
              directiveGuards.push(guardInvoke);
            }
          }
        }


i => i.name === guard.inputName


(i: TmplAstTextAttribute|TmplAstBoundAttribute): i is TmplAstBoundAttribute =>
                      i instanceof TmplAstBoundAttribute && i.name === guard.inputName


(expr, dirGuard) =>
              ts.createBinary(expr, ts.SyntaxKind.AmpersandAmpersandToken, dirGuard)


param => ts.createTypeReferenceNode(param.name, undefined)


() => ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)


ast => this.resolve(ast)


arg => this.translate(arg)


input => {
    const propertyName = ts.createStringLiteral(input.field);

    if (input.type === 'binding') {
      // For bound inputs, the property is assigned the binding expression.
      let expr = input.expression;
      if (!tcb.env.config.checkTypeOfInputBindings) {
        // If checking the type of bindings is disabled, cast the resulting expression to 'any'
        // before the assignment.
        expr = tsCastToAny(expr);
      } else if (!tcb.env.config.strictNullInputBindings) {
        // If strict null checks are disabled, erase `null` and `undefined` from the type by
        // wrapping the expression in a non-null assertion.
        expr = ts.createNonNullExpression(expr);
      }

      const assignment = ts.createPropertyAssignment(propertyName, wrapForDiagnostics(expr));
      addParseSpanInfo(assignment, input.sourceSpan);
      return assignment;
    } else {
      // A type constructor is required to be called with all input properties, so any unset
      // inputs are simply assigned a value of type `any` to ignore them.
      return ts.createPropertyAssignment(propertyName, NULL_AS_ANY);
    }
  }


key => {
    Array.isArray(inputs[key]) ? propMatch.set(inputs[key][0], key) :
                                 propMatch.set(inputs[key] as string, key);
  }


/**
 * A code generation operation that's involved in the construction of a Type Check Block.
 *
 * The generation of a TCB is non-linear. Bindings within a template may result in the need to
 * construct certain types earlier than they otherwise would be constructed. That is, if the
 * generation of a TCB for a template is broken down into specific operations (constructing a
 * directive, extracting a variable from a let- operation, etc), then it's possible for operations
 * earlier in the sequence to depend on operations which occur later in the sequence.
 *
 * `TcbOp` abstracts the different types of operations which are required to convert a template into
 * a TCB. This allows for two phases of processing for the template, where 1) a linear sequence of
 * `TcbOp`s is generated, and then 2) these operations are executed, not necessarily in linear
 * order.
 *
 * Each `TcbOp` may insert statements into the body of the TCB, and also optionally return a
 * `ts.Expression` which can be used to reference the operation's result.
 */
abstract class TcbOp {
  abstract execute(): ts.Expression|null;

  /**
   * Replacement value or operation used while this `TcbOp` is executing (i.e. to resolve circular
   * references during its execution).
   *
   * This is usually a `null!` expression (which asks TS to infer an appropriate type), but another
   * `TcbOp` can be returned in cases where additional code generation is necessary to deal with
   * circular references.
   */
  circularFallback(): TcbOp|ts.Expression {
    return INFER_TYPE_FOR_CIRCULAR_OP_EXPR;
  }
}


/**
 * A `TcbOp` which creates an expression for a native DOM element (or web component) from a
 * `TmplAstElement`.
 *
 * Executing this operation returns a reference to the element variable.
 */
class TcbElementOp extends TcbOp {
  constructor(private tcb: Context, private scope: Scope, private element: TmplAstElement) {
    super();
  }

  execute(): ts.Identifier {
    const id = this.tcb.allocateId();
    // Add the declaration of the element using document.createElement.
    const initializer = tsCreateElement(this.element.name);
    addParseSpanInfo(initializer, this.element.startSourceSpan || this.element.sourceSpan);
    this.scope.addStatement(tsCreateVariable(id, initializer));
    return id;
  }
}


/**
 * A `TcbOp` which creates an expression for particular let- `TmplAstVariable` on a
 * `TmplAstTemplate`'s context.
 *
 * Executing this operation returns a reference to the variable variable (lol).
 */
class TcbVariableOp extends TcbOp {
  constructor(
      private tcb: Context, private scope: Scope, private template: TmplAstTemplate,
      private variable: TmplAstVariable) {
    super();
  }

  execute(): ts.Identifier {
    // Look for a context variable for the template.
    const ctx = this.scope.resolve(this.template);

    // Allocate an identifier for the TmplAstVariable, and initialize it to a read of the variable
    // on the template context.
    const id = this.tcb.allocateId();
    const initializer = ts.createPropertyAccess(
        /* expression */ ctx,
        /* name */ this.variable.value || '$implicit');
    addParseSpanInfo(initializer, this.variable.sourceSpan);

    // Declare the variable, and return its identifier.
    this.scope.addStatement(tsCreateVariable(id, initializer));
    return id;
  }
}


/**
 * A `TcbOp` which generates a variable for a `TmplAstTemplate`'s context.
 *
 * Executing this operation returns a reference to the template's context variable.
 */
class TcbTemplateContextOp extends TcbOp {
  constructor(private tcb: Context, private scope: Scope) {
    super();
  }

  execute(): ts.Identifier {
    // Allocate a template ctx variable and declare it with an 'any' type. The type of this variable
    // may be narrowed as a result of template guard conditions.
    const ctx = this.tcb.allocateId();
    const type = ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
    this.scope.addStatement(tsDeclareVariable(ctx, type));
    return ctx;
  }
}


/**
 * A `TcbOp` which descends into a `TmplAstTemplate`'s children and generates type-checking code for
 * them.
 *
 * This operation wraps the children's type-checking code in an `if` block, which may include one
 * or more type guard conditions that narrow types within the template body.
 */
class TcbTemplateBodyOp extends TcbOp {
  constructor(private tcb: Context, private scope: Scope, private template: TmplAstTemplate) {
    super();
  }
  execute(): null {
    // An `if` will be constructed, within which the template's children will be type checked. The
    // `if` is used for two reasons: it creates a new syntactic scope, isolating variables declared
    // in the template's TCB from the outer context, and it allows any directives on the templates
    // to perform type narrowing of either expressions or the template's context.
    //
    // The guard is the `if` block's condition. It's usually set to `true` but directives that exist
    // on the template can trigger extra guard expressions that serve to narrow types within the
    // `if`. `guard` is calculated by starting with `true` and adding other conditions as needed.
    // Collect these into `guards` by processing the directives.
    const directiveGuards: ts.Expression[] = [];

    const directives = this.tcb.boundTarget.getDirectivesOfNode(this.template);
    if (directives !== null) {
      for (const dir of directives) {
        const dirInstId = this.scope.resolve(this.template, dir);
        const dirId =
            this.tcb.env.reference(dir.ref as Reference<ClassDeclaration<ts.ClassDeclaration>>);

        // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
        // the expression passed to an @Input of the directive. Scan the directive to see if it has
        // any template guards, and generate them if needed.
        dir.ngTemplateGuards.forEach(guard => {
          // For each template guard function on the directive, look for a binding to that input.
          const boundInput = this.template.inputs.find(i => i.name === guard.inputName) ||
              this.template.templateAttrs.find(
                  (i: TmplAstTextAttribute|TmplAstBoundAttribute): i is TmplAstBoundAttribute =>
                      i instanceof TmplAstBoundAttribute && i.name === guard.inputName);
          if (boundInput !== undefined) {
            // If there is such a binding, generate an expression for it.
            const expr = tcbExpression(boundInput.value, this.tcb, this.scope);

            // The expression has already been checked in the type constructor invocation, so
            // it should be ignored when used within a template guard.
            ignoreDiagnostics(expr);

            if (guard.type === 'binding') {
              // Use the binding expression itself as guard.
              directiveGuards.push(expr);
            } else {
              // Call the guard function on the directive with the directive instance and that
              // expression.
              const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
                dirInstId,
                expr,
              ]);
              addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
              directiveGuards.push(guardInvoke);
            }
          }
        });

        // The second kind of guard is a template context guard. This guard narrows the template
        // rendering context variable `ctx`.
        if (dir.hasNgTemplateContextGuard && this.tcb.env.config.applyTemplateContextGuards) {
          const ctx = this.scope.resolve(this.template);
          const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
          addParseSpanInfo(guardInvoke, this.template.sourceSpan);
          directiveGuards.push(guardInvoke);
        }
      }
    }

    // By default the guard is simply `true`.
    let guard: ts.Expression|null = null;

    // If there are any guards from directives, use them instead.
    if (directiveGuards.length > 0) {
      // Pop the first value and use it as the initializer to reduce(). This way, a single guard
      // will be used on its own, but two or more will be combined into binary AND expressions.
      guard = directiveGuards.reduce(
          (expr, dirGuard) =>
              ts.createBinary(expr, ts.SyntaxKind.AmpersandAmpersandToken, dirGuard),
          directiveGuards.pop()!);
    }

    // Create a new Scope for the template. This constructs the list of operations for the template
    // children, as well as tracks bindings within the template.
    const tmplScope = Scope.forNodes(this.tcb, this.scope, this.template, guard);

    // Render the template's `Scope` into a block.
    let tmplBlock: ts.Statement = ts.createBlock(tmplScope.render());
    if (guard !== null) {
      // The scope has a guard that needs to be applied, so wrap the template block into an `if`
      // statement containing the guard expression.
      tmplBlock = ts.createIf(/* expression */ guard, /* thenStatement */ tmplBlock);
    }
    this.scope.addStatement(tmplBlock);

    return null;
  }
}


/**
 * A `TcbOp` which renders a text binding (interpolation) into the TCB.
 *
 * Executing this operation returns nothing.
 */
class TcbTextInterpolationOp extends TcbOp {
  constructor(private tcb: Context, private scope: Scope, private binding: TmplAstBoundText) {
    super();
  }

  execute(): null {
    const expr = tcbExpression(this.binding.value, this.tcb, this.scope);
    this.scope.addStatement(ts.createExpressionStatement(expr));
    return null;
  }
}


/**
 * A `TcbOp` which constructs an instance of a directive with types inferred from its inputs, which
 * also checks the bindings to the directive in the process.
 *
 * Executing this operation returns a reference to the directive instance variable with its inferred
 * type.
 */
class TcbDirectiveOp extends TcbOp {
  constructor(
      private tcb: Context, private scope: Scope, private node: TmplAstTemplate|TmplAstElement,
      private dir: TypeCheckableDirectiveMeta) {
    super();
  }

  execute(): ts.Identifier {
    const id = this.tcb.allocateId();
    // Process the directive and construct expressions for each of its bindings.
    const inputs = tcbGetDirectiveInputs(this.node, this.dir, this.tcb, this.scope);

    // Call the type constructor of the directive to infer a type, and assign the directive
    // instance.
    const typeCtor = tcbCallTypeCtor(this.dir, this.tcb, inputs);
    addParseSpanInfo(typeCtor, this.node.sourceSpan);
    this.scope.addStatement(tsCreateVariable(id, typeCtor));
    return id;
  }

  circularFallback(): TcbOp {
    return new TcbDirectiveCircularFallbackOp(this.tcb, this.scope, this.node, this.dir);
  }
}


/**
 * A `TcbOp` which is used to generate a fallback expression if the inference of a directive type
 * via `TcbDirectiveOp` requires a reference to its own type. This can happen using a template
 * reference:
 *
 * ```html
 * <some-cmp #ref [prop]="ref.foo"></some-cmp>
 * ```
 *
 * In this case, `TcbDirectiveCircularFallbackOp` will add a second inference of the directive type
 * to the type-check block, this time calling the directive's type constructor without any input
 * expressions. This infers the widest possible supertype for the directive, which is used to
 * resolve any recursive references required to infer the real type.
 */
class TcbDirectiveCircularFallbackOp extends TcbOp {
  constructor(
      private tcb: Context, private scope: Scope, private node: TmplAstTemplate|TmplAstElement,
      private dir: TypeCheckableDirectiveMeta) {
    super();
  }

  execute(): ts.Identifier {
    const id = this.tcb.allocateId();
    const typeCtor = this.tcb.env.typeCtorFor(this.dir);
    const circularPlaceholder = ts.createCall(
        typeCtor, /* typeArguments */ undefined, [ts.createNonNullExpression(ts.createNull())]);
    this.scope.addStatement(tsCreateVariable(id, circularPlaceholder));
    return id;
  }
}


/**
 * A `TcbOp` which feeds elements and unclaimed properties to the `DomSchemaChecker`.
 *
 * The DOM schema is not checked via TCB code generation. Instead, the `DomSchemaChecker` ingests
 * elements and property bindings and accumulates synthetic `ts.Diagnostic`s out-of-band. These are
 * later merged with the diagnostics generated from the TCB.
 *
 * For convenience, the TCB iteration of the template is used to drive the `DomSchemaChecker` via
 * the `TcbDomSchemaCheckerOp`.
 */
class TcbDomSchemaCheckerOp extends TcbOp {
  constructor(
      private tcb: Context, private element: TmplAstElement, private checkElement: boolean,
      private claimedInputs: Set<string>) {
    super();
  }

  execute(): ts.Expression|null {
    if (this.checkElement) {
      this.tcb.domSchemaChecker.checkElement(this.tcb.id, this.element, this.tcb.schemas);
    }

    // TODO(alxhub): this could be more efficient.
    for (const binding of this.element.inputs) {
      if (binding.type === BindingType.Property && this.claimedInputs.has(binding.name)) {
        // Skip this binding as it was claimed by a directive.
        continue;
      }

      if (binding.type === BindingType.Property) {
        if (binding.name !== 'style' && binding.name !== 'class') {
          // A direct binding to a property.
          const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
          this.tcb.domSchemaChecker.checkProperty(
              this.tcb.id, this.element, propertyName, binding.sourceSpan, this.tcb.schemas);
        }
      }
    }
    return null;
  }
}