@Injectable({
    providedIn: 'root'
})
export class WebsocketService implements IWebsocketService, OnDestroy {

    private config: WebSocketSubjectConfig<IWsMessage<any>>;

    private websocketSub: SubscriptionLike;
    private statusSub: SubscriptionLike;

    private reconnection$: Observable<number>;
    private websocket$: WebSocketSubject<IWsMessage<any>>;
    private connection$: Observer<boolean>;
    private wsMessages$: Subject<IWsMessage<any>>;

    private reconnectInterval: number;
    private reconnectAttempts: number;
    private isConnected: boolean;

    public status: Observable<boolean>;

    constructor(@Inject(config) private wsConfig: WebSocketConfig) {
        this.wsMessages$ = new Subject<IWsMessage<any>>();

        this.reconnectInterval = wsConfig.reconnectInterval || 5000; // pause between connections
        this.reconnectAttempts = wsConfig.reconnectAttempts || 10; // number of connection attempts

        this.config = {
            url: wsConfig.url,
            closeObserver: {
                next: (event: CloseEvent) => {
                    this.websocket$ = null;
                    this.connection$.next(false);
                }
            },
            openObserver: {
                next: (event: Event) => {
                    console.log('WebSocket connected!');
                    this.connection$.next(true);
                }
            }
        };

        // connection status
        this.status = new Observable<boolean>((observer) => {
            this.connection$ = observer;
        }).pipe(share(), distinctUntilChanged());

        // run reconnect if not connection
        this.statusSub = this.status
            .subscribe((isConnected) => {
                this.isConnected = isConnected;

                if (!this.reconnection$ && typeof(isConnected) === 'boolean' && !isConnected) {
                    this.reconnect();
                }
            });

        this.websocketSub = this.wsMessages$.subscribe(
            null, (error: ErrorEvent) => console.error('WebSocket error!', error)
        );

        this.connect();
    }

    ngOnDestroy() {
        this.websocketSub.unsubscribe();
        this.statusSub.unsubscribe();
    }

    /*
    * connect to WebSocked
    * */
    private connect(): void {
        this.websocket$ = new WebSocketSubject(this.config);

        this.websocket$.subscribe(
            (message) => this.wsMessages$.next(message),
            (error: Event) => {
                if (!this.websocket$) {
                    // run reconnect if errors
                    this.reconnect();
                }
            });
    }

    /*
    * reconnect if not connecting or errors
    * */
    private reconnect(): void {
        this.reconnection$ = interval(this.reconnectInterval)
            .pipe(takeWhile((v, index) => index < this.reconnectAttempts && !this.websocket$));

        this.reconnection$.subscribe(
            () => this.connect(),
            null,
            () => {
                // Subject complete if reconnect attemts ending
                this.reconnection$ = null;

                if (!this.websocket$) {
                    this.wsMessages$.complete();
                    this.connection$.complete();
                }
            });
    }

    /*
    * on message event
    * */
    public on<T>(event: string): Observable<T> {
        if (event) {
            return this.wsMessages$.pipe(
                filter((message: IWsMessage<T>) => message.event === event),
                map((message: IWsMessage<T>) => message.data)
            );
        }
    }

    /*
    * on message to server
    * */
    public send(event: string, data: any = {}): void {
        if (event && this.isConnected) {
            this.websocket$.next(<any>JSON.stringify({ event, data }));
        } else {
            console.error('Send error!');
        }
    }

}


ngOnDestroy() {
        this.websocketSub.unsubscribe();
        this.statusSub.unsubscribe();
    }


/*
    * connect to WebSocked
    * */
private connect(): void {
        this.websocket$ = new WebSocketSubject(this.config);

        this.websocket$.subscribe(
            (message) => this.wsMessages$.next(message),
            (error: Event) => {
                if (!this.websocket$) {
                    // run reconnect if errors
                    this.reconnect();
                }
            });
    }


/*
    * reconnect if not connecting or errors
    * */
private reconnect(): void {
        this.reconnection$ = interval(this.reconnectInterval)
            .pipe(takeWhile((v, index) => index < this.reconnectAttempts && !this.websocket$));

        this.reconnection$.subscribe(
            () => this.connect(),
            null,
            () => {
                // Subject complete if reconnect attemts ending
                this.reconnection$ = null;

                if (!this.websocket$) {
                    this.wsMessages$.complete();
                    this.connection$.complete();
                }
            });
    }


/*
    * on message event
    * */
public on<T>(event: string): Observable<T> {
        if (event) {
            return this.wsMessages$.pipe(
                filter((message: IWsMessage<T>) => message.event === event),
                map((message: IWsMessage<T>) => message.data)
            );
        }
    }


/*
    * on message to server
    * */
public send(event: string, data: any = {}): void {
        if (event && this.isConnected) {
            this.websocket$.next(<any>JSON.stringify({ event, data }));
        } else {
            console.error('Send error!');
        }
    }


({ editPlanObj, isEdit }) => {
    const values = {
      planName: '',
      sourceCluster: null,
      targetCluster: null,
      selectedNamespaces: [],
      selectedStorage: null,
      persistentVolumes: [],
    };
    if (editPlanObj && isEdit) {
      values.planName = editPlanObj.metadata.name || '';
      values.sourceCluster = editPlanObj.spec.srcMigClusterRef.name || null;
      values.targetCluster = editPlanObj.spec.destMigClusterRef.name || null;
      values.selectedNamespaces = editPlanObj.spec.namespaces || [];
      values.selectedStorage = editPlanObj.spec.migStorageRef.name || null;
      values.persistentVolumes = editPlanObj.spec.persistentVolumes || [];
    }

    return values;
  }


values => {
    const errors: any = {};

    if (!values.planName) {
      errors.planName = 'Required';
    } else if (!utils.testDNS1123(values.planName)) {
      errors.planName = utils.DNS1123Error(values.planName);
    }
    if (!values.sourceCluster) {
      errors.sourceCluster = 'Required';
    }
    if (!values.selectedNamespaces || values.selectedNamespaces.length === 0) {
      errors.selectedNamespaces = 'Required';
    }
    if (!values.targetCluster) {
      errors.targetCluster = 'Required';
    }
    if (!values.selectedStorage) {
      errors.selectedStorage = 'Required';
    }
    return errors;
  }


() => {
    return null;
  }


state => {
  const filteredSourceClusterNamespaces = state.plan.sourceClusterNamespaces.filter(ns => {
    const rejectedRegex = [
      RegExp('^kube-.*', 'i'),
      RegExp('^openshift-.*', 'i'),
      RegExp('^openshift$', 'i'),
      RegExp('^velero$', 'i'),
      RegExp('^default$', 'i'),
    ];

    // Short circuit the regex check if any of them match a rejected regex and filter it out
    return !rejectedRegex.some(rx => rx.test(ns.name));
  });

  return {
    planName: '',
    sourceCluster: null,
    targetCluster: null,
    selectedNamespaces: [],
    selectedStorage: '',
    persistentVolumes: [],
    isPVPolling: state.plan.isPVPolling,
    isPollingPlans: state.plan.isPolling,
    isPollingClusters: state.cluster.isPolling,
    isPollingStorage: state.storage.isPolling,
    isPollingStatus: state.plan.isPollingStatus,
    isFetchingNamespaceList: state.plan.isFetchingNamespaceList,
    sourceClusterNamespaces: filteredSourceClusterNamespaces,
    isFetchingPVResources: state.plan.isFetchingPVResources,
    isPVError: state.plan.isPVError,
    currentPlan: planSelectors.getCurrentPlan(state),
    currentPlanStatus: state.plan.currentPlanStatus,
    pvResourceList: state.plan.pvResourceList,
  };
}


ns => {
    const rejectedRegex = [
      RegExp('^kube-.*', 'i'),
      RegExp('^openshift-.*', 'i'),
      RegExp('^openshift$', 'i'),
      RegExp('^velero$', 'i'),
      RegExp('^default$', 'i'),
    ];

    // Short circuit the regex check if any of them match a rejected regex and filter it out
    return !rejectedRegex.some(rx => rx.test(ns.name));
  }


rx => rx.test(ns.name)


dispatch => {
  return {
    addPlan: (plan) => dispatch(planOperations.addPlan(plan)),
    fetchNamespacesForCluster: clusterName => {
      dispatch(planOperations.fetchNamespacesForCluster(clusterName));
    },
    getPVResourcesRequest: (pvList, clusterName) => dispatch(PlanActions.getPVResourcesRequest(pvList, clusterName)),
    startPlanStatusPolling: (planName: string) => dispatch(PlanActions.startPlanStatusPolling(planName)),
    stopPlanStatusPolling: (planName: string) => dispatch(PlanActions.stopPlanStatusPolling(planName)),
    planUpdateRequest: (values, isRerunPVDiscovery) =>
      dispatch(PlanActions.planUpdateRequest(values, isRerunPVDiscovery)),
    resetCurrentPlan: () => dispatch(PlanActions.resetCurrentPlan()),
    setCurrentPlan: (plan) => dispatch(PlanActions.setCurrentPlan(plan)),
    updateCurrentPlanStatus: (status) => dispatch(PlanActions.updateCurrentPlanStatus(status)),
    pvUpdatePollStop: () => dispatch(PlanActions.pvUpdatePollStop()),
  };
}


(plan) => dispatch(planOperations.addPlan(plan))


clusterName => {
      dispatch(planOperations.fetchNamespacesForCluster(clusterName));
    }


(pvList, clusterName) => dispatch(PlanActions.getPVResourcesRequest(pvList, clusterName))


(planName: string) => dispatch(PlanActions.startPlanStatusPolling(planName))


(planName: string) => dispatch(PlanActions.stopPlanStatusPolling(planName))


(values, isRerunPVDiscovery) =>
      dispatch(PlanActions.planUpdateRequest(values, isRerunPVDiscovery))


() => dispatch(PlanActions.resetCurrentPlan())


(plan) => dispatch(PlanActions.setCurrentPlan(plan))


(status) => dispatch(PlanActions.updateCurrentPlanStatus(status))


() => dispatch(PlanActions.pvUpdatePollStop())


export interface IFormValues {
  planName: string;
  sourceCluster: string;
  targetCluster: string;
  selectedStorage: string;
  selectedNamespaces: any[];
}


export interface IOtherProps {
  clusterList: any[];
  planList: any[];
  storageList: any[];
  isFetchingPVList: boolean;
  isPVPolling: boolean;
  isPollingStatus: boolean;
  isPVError: boolean;
  isCheckingPlanStatus: boolean;
  isFetchingPVResources: boolean;
  isFetchingNamespaceList: boolean;
  isOpen: boolean;
  isPollingStorage: boolean;
  isPollingClusters: boolean;
  isPollingPlans: boolean;
  currentPlan: any;
  currentPlanStatus: any;
  startPlanStatusPolling: (planName) => void;
  stopPlanStatusPolling: (planName) => void;
  pvUpdatePollStop: () => void;
  planUpdateRequest: (values, isRerunPVDiscovery) => void;
  resetCurrentPlan: () => void;
  setCurrentPlan: (plan) => void;
  fetchNamespacesForCluster: () => void;
  getPVResourcesRequest: () => void;
  addPlan: (planValues) => void;
  sourceClusterNamespaces: any[];
  pvResourceList: any[];
  onHandleWizardModalClose: () => void;
  editPlanObj?: any;
  isEdit: boolean;
  updateCurrentPlanStatus: any;
}


(resolve) => {
			this._objects = new Objects({
				connection: {
					type: "file",
					host: "127.0.0.1",
					port: 19001,
					user: "",
					pass: "",
					noFileCache: false,
					connectTimeout: 2000,
				},
				logger,
				connected: () => {
					debug("  => done!");
					resolve();
				},
				change: this.emit.bind(this, "objectChange"),
			});
			this._objects.subscribe("*");
		}


() => {
					debug("  => done!");
					resolve();
				}


(resolve) => {
			this._states = new States({
				connection: {
					type: "file",
					host: "127.0.0.1",
					port: 19000,
					options: {
						auth_pass: null,
						retry_max_delay: 15000,
					},
				},
				logger,
				connected: () => {
					debug("  => done!");
					resolve();
				},
				change: this.emit.bind(this, "stateChange"),
			});
			this._states.subscribe("*");
		}


(code: number | undefined, signal: string): void => {
			this._adapterProcess!.removeAllListeners();
			this._adapterExit = code != undefined ? code : signal;
			this.emit("failed", this._adapterExit);
		}


(resolve, reject) => {
			this.on("stateChange", async (id, state) => {
				if (
					id === `system.adapter.${this.adapterName}.0.alive` &&
					state &&
					state.val === true
				) {
					resolve();
				}
			})
				.on("failed", (code) => {
					reject(
						new Error(
							`The adapter startup was interrupted unexpectedly with ${
								typeof code === "number" ? "code" : "signal"
							} ${code}`,
						),
					);
				})
				.startAdapter(env);
		}


async (id, state) => {
				if (
					id === `system.adapter.${this.adapterName}.0.alive` &&
					state &&
					state.val === true
				) {
					resolve();
				}
			}


(code) => {
					reject(
						new Error(
							`The adapter startup was interrupted unexpectedly with ${
								typeof code === "number" ? "code" : "signal"
							} ${code}`,
						),
					);
				}


(resolve) => {
			const onClose = (
				code: number | undefined,
				signal: string,
			): void => {
				if (!this._adapterProcess) return;
				this._adapterProcess.removeAllListeners();

				this._adapterExit = code != undefined ? code : signal;
				this._adapterProcess = undefined;
				debug("Adapter process terminated:");
				debug(`  Code:   ${code}`);
				debug(`  Signal: ${signal}`);
				resolve();
			};

			this._adapterProcess!.removeAllListeners()
				.on("close", onClose)
				.on("exit", onClose)
				.kill("SIGTERM");
		}


(
				code: number | undefined,
				signal: string,
			): void => {
				if (!this._adapterProcess) return;
				this._adapterProcess.removeAllListeners();

				this._adapterExit = code != undefined ? code : signal;
				this._adapterProcess = undefined;
				debug("Adapter process terminated:");
				debug(`  Code:   ${code}`);
				debug(`  Signal: ${signal}`);
				resolve();
			}


(resolve) => {
			this._objects.setObject(
				fromAdapterID,
				{
					common: {},
					type: "instance",
				},
				() => {
					this._states.subscribeMessage(fromAdapterID);
					resolve();
				},
			);
		}


() => {
					this._states.subscribeMessage(fromAdapterID);
					resolve();
				}


(
			id,
			state,
		) => {
			if (id === `messagebox.${fromAdapterID}`) {
				callback((state as any).message);
				this.removeListener("stateChange", stateChangedHandler);
			}
		}


(err: any, id: any) => console.log("published message " + id)


export interface TestHarness {
	on(event: "objectChange", handler: ioBroker.ObjectChangeHandler): this;
	on(event: "stateChange", handler: ioBroker.StateChangeHandler): this;
	on(event: "failed", handler: (codeOrSignal: number | string) => void): this;
}


/** Creates the objects DB and sets up listeners for it */
private async createObjectsDB(): Promise<void> {
		debug("creating objects DB");

		const Objects = require(path.join(
			this.testControllerDir,
			"lib/objects/objectsInMemServer",
		));

		return new Promise<void>((resolve) => {
			this._objects = new Objects({
				connection: {
					type: "file",
					host: "127.0.0.1",
					port: 19001,
					user: "",
					pass: "",
					noFileCache: false,
					connectTimeout: 2000,
				},
				logger,
				connected: () => {
					debug("  => done!");
					resolve();
				},
				change: this.emit.bind(this, "objectChange"),
			});
			this._objects.subscribe("*");
		});
	}


/** Creates the states DB and sets up listeners for it */
private async createStatesDB(): Promise<void> {
		debug("creating states DB");

		const States = require(path.join(
			this.testControllerDir,
			"lib/states/statesInMemServer",
		));

		return new Promise<void>((resolve) => {
			this._states = new States({
				connection: {
					type: "file",
					host: "127.0.0.1",
					port: 19000,
					options: {
						auth_pass: null,
						retry_max_delay: 15000,
					},
				},
				logger,
				connected: () => {
					debug("  => done!");
					resolve();
				},
				change: this.emit.bind(this, "stateChange"),
			});
			this._states.subscribe("*");
		});
	}


/** Checks if the controller instance is running */
public isControllerRunning(): boolean {
		return !!this._objects || !!this._states;
	}


/** Starts the controller instance by creating the databases */
public async startController(): Promise<void> {
		debug("starting controller instance...");
		if (this.isControllerRunning())
			throw new Error("The Controller is already running!");
		await this.createObjectsDB();
		await this.createStatesDB();
		debug("controller instance created");
	}


/** Stops the controller instance (and the adapter if it is running) */
public async stopController(): Promise<void> {
		if (!this.isControllerRunning()) return;

		debug("Stopping controller instance...");
		if (this._objects) {
			// Set adapter instance disabled
			this._objects.setObject(`system.adapter.${this.adapterName}.0`, {
				common: {
					enabled: false,
				},
			});
		}

		// Give the adapter time to stop, but maximum 5s
		if (!this.didAdapterStop()) {
			debug("Stopping adapter instance...");
			await Promise.race([this.stopAdapter(), wait(5000)]);
			if (this.isAdapterRunning()) {
				debug("Adapter did not terminate, killing it");
				this._adapterProcess!.kill("SIGKILL");
			} else {
				debug("Adapter terminated");
			}
		} else {
			debug("Adapter failed to start - no need to terminate!");
		}

		if (this._objects) {
			this._objects.destroy();
			this._objects = null;
		}
		if (this._states) {
			this._states.destroy();
			this._states = null;
		}
		debug("Controller instance stopped");
	}


/**
	 * Starts the adapter in a separate process and monitors its status
	 * @param env Additional environment variables to set
	 */
public async startAdapter(env: NodeJS.ProcessEnv = {}): Promise<void> {
		if (this.isAdapterRunning())
			throw new Error("The adapter is already running!");
		else if (this.didAdapterStop())
			throw new Error(
				"This test harness has already been used. Please create a new one for each test!",
			);

		const mainFileAbsolute = await locateAdapterMainFile(
			this.testAdapterDir,
		);
		const mainFileRelative = path.relative(
			this.testAdapterDir,
			mainFileAbsolute,
		);

		const onClose = (code: number | undefined, signal: string): void => {
			this._adapterProcess!.removeAllListeners();
			this._adapterExit = code != undefined ? code : signal;
			this.emit("failed", this._adapterExit);
		};

		this._adapterProcess = spawn(
			isWindows ? "node.exe" : "node",
			[mainFileRelative, "--console"],
			{
				cwd: this.testAdapterDir,
				stdio: ["inherit", "inherit", "inherit"],
				env: { ...process.env, ...env },
			},
		)
			.on("close", onClose)
			.on("exit", onClose);
	}


/**
	 * Starts the adapter in a separate process and resolves after it has started
	 * @param env Additional environment variables to set
	 */
public async startAdapterAndWait(
		env: NodeJS.ProcessEnv = {},
	): Promise<void> {
		return new Promise<void>((resolve, reject) => {
			this.on("stateChange", async (id, state) => {
				if (
					id === `system.adapter.${this.adapterName}.0.alive` &&
					state &&
					state.val === true
				) {
					resolve();
				}
			})
				.on("failed", (code) => {
					reject(
						new Error(
							`The adapter startup was interrupted unexpectedly with ${
								typeof code === "number" ? "code" : "signal"
							} ${code}`,
						),
					);
				})
				.startAdapter(env);
		});
	}


/** Tests if the adapter process is still running */
public isAdapterRunning(): boolean {
		return !!this._adapterProcess;
	}


/** Tests if the adapter process has already exited */
public didAdapterStop(): boolean {
		return this._adapterExit != undefined;
	}


/** Stops the adapter process */
public stopAdapter(): Promise<void> | undefined {
		if (!this.isAdapterRunning()) return;

		return new Promise<void>((resolve) => {
			const onClose = (
				code: number | undefined,
				signal: string,
			): void => {
				if (!this._adapterProcess) return;
				this._adapterProcess.removeAllListeners();

				this._adapterExit = code != undefined ? code : signal;
				this._adapterProcess = undefined;
				debug("Adapter process terminated:");
				debug(`  Code:   ${code}`);
				debug(`  Signal: ${signal}`);
				resolve();
			};

			this._adapterProcess!.removeAllListeners()
				.on("close", onClose)
				.on("exit", onClose)
				.kill("SIGTERM");
		});
	}


/**
	 * Updates the adapter config. The changes can be a subset of the target object
	 */
public async changeAdapterConfig(
		appName: string,
		testDir: string,
		adapterName: string,
		changes: any,
	): Promise<void> {
		const objects = await this.dbConnection.readObjectsDB();
		const adapterInstanceId = `system.adapter.${adapterName}.0`;
		if (objects && adapterInstanceId in objects) {
			const target = objects[adapterInstanceId];
			extend(target, changes);
			await this.dbConnection.writeObjectsDB(objects);
		}
	}


/** Enables the sendTo method */
public enableSendTo(): Promise<void> {
		return new Promise<void>((resolve) => {
			this._objects.setObject(
				fromAdapterID,
				{
					common: {},
					type: "instance",
				},
				() => {
					this._states.subscribeMessage(fromAdapterID);
					resolve();
				},
			);
		});
	}


/** Sends a message to an adapter instance */
public sendTo(
		target: string,
		command: string,
		message: any,
		callback: ioBroker.MessageCallback,
	): void {
		const stateChangedHandler: ioBroker.StateChangeHandler = (
			id,
			state,
		) => {
			if (id === `messagebox.${fromAdapterID}`) {
				callback((state as any).message);
				this.removeListener("stateChange", stateChangedHandler);
			}
		};
		this.addListener("stateChange", stateChangedHandler);

		this._states.pushMessage(
			`system.adapter.${target}`,
			{
				command: command,
				message: message,
				from: fromAdapterID,
				callback: {
					message: message,
					id: this.sendToID++,
					ack: false,
					time: Date.now(),
				},
			},
			(err: any, id: any) => console.log("published message " + id),
		);
	}


({
  commentsLoading,
  generalLocation,
  isHrAdvisor,
  jobId,
  locationMessages,
  totalActivities,
  filterComments,
}) => {
  const intl = useIntl();
  const locationOptions = Object.values(LocationId)
    .filter((location) => hasKey(locationMessages, location))
    .map((location) => ({
      value: location,
      label: intl.formatMessage(locationMessages[location]),
    }));
  return (

          <button
            aria-expanded="false"
            data-c-accordion-trigger
            tabIndex={0}
            type="button"
            data-c-background="c1(100)"
            data-c-padding="all(1)"
          >

                <FormattedMessage
                  id="activityfeed.header"
                  defaultMessage="Click to View Comments {totalActivities}"
                  description="The activity feed header."
                  values={{
                    totalActivities: commentsLoading ? (
                      <Icon
                        icon="fa fa-spinner fa-spin"
                        accessibleText={intl.formatMessage(
                          messages.loadingIcon,
                        )}
                        sematicIcon
                      />


(location) => hasKey(locationMessages, location)


(location) => ({
      value: location,
      label: intl.formatMessage(locationMessages[location]),
    })


(
  state: RootState,
  {
    filterComments = (): boolean => true,
  }: { filterComments?: (comment: Comment) => boolean },
): {
  commentsLoading: boolean;
  totalActivities: number;
} => ({
  commentsLoading: fetchingComments(state),
  totalActivities: getComments(state).filter(filterComments).length,
})


(): boolean => true


interface ActivityFeedProps {
  commentsLoading: boolean;
  generalLocation: string;
  isHrAdvisor: boolean;
  jobId: number;
  locationMessages: { [LocationId: string]: MessageDescriptor };
  totalActivities: number;
  filterComments?: (comment: Comment) => boolean;
}


(res) => {
            console.log('Succesfully sent message: ', res);
        }


(error) => {
            console.log('Error sending message: ', error);
        }


export class Notifier implements NotifierInterface{
    private serviceAccount : admin.ServiceAccount;
    private fcm : admin.messaging.Messaging;
    private app : admin.app.App;

    public constructor(){
        this.serviceAccount = ShepardServiceAccount as admin.ServiceAccount;
        this.app = admin.initializeApp({
            credential: admin.credential.cert(this.serviceAccount)
        });
        this.fcm = admin.messaging();

    }

    public sendToTopic(topic :string, message : MessagingPayload) :  void{
        this.fcm.sendToTopic(topic, message).then((res) => {
            console.log('Succesfully sent message: ', res);
        }).catch((error) => {
            console.log('Error sending message: ', error);
        })
    }
}


public sendToTopic(topic :string, message : MessagingPayload) :  void{
        this.fcm.sendToTopic(topic, message).then((res) => {
            console.log('Succesfully sent message: ', res);
        }).catch((error) => {
            console.log('Error sending message: ', error);
        })
    }


(config: OpsWorksCMClientConfig = {}) => ({
  apiVersion: "2016-11-01",
  disableHostPrefix: config.disableHostPrefix ?? false,
  logger: config.logger ?? ({} as __Logger),
  regionInfoProvider: config.regionInfoProvider ?? defaultRegionInfoProvider,
  serviceId: config.serviceId ?? "OpsWorksCM",
  urlParser: config.urlParser ?? parseUrl,
})


(el: Cash, value: string): void => {
  const codeMirrorEl: HTMLElementWithCodeMirror = el.siblings('.CodeMirror').get(0) as HTMLElement
  if (codeMirrorEl && codeMirrorEl.CodeMirror) {
    codeMirrorEl.CodeMirror.setValue(value)
  }
}


(el: Cash): string => {
  const codeMirrorEl: HTMLElementWithCodeMirror = el.siblings('.CodeMirror').get(0) as HTMLElement
  if (codeMirrorEl && codeMirrorEl.CodeMirror) {
    return codeMirrorEl.CodeMirror.getValue()
  }
  return (el.val() as string)
}


(ta: Cash): void => {
  try {
    const editor = CodeMirror.fromTextArea(ta.get(0) as HTMLTextAreaElement, { lineNumbers: true })
    setTimeout(() => { editor.refresh() }, 0)
    // eslint-disable-next-line no-empty
  } catch (e) {}
}


() => { editor.refresh() }


(el: Cash, event: string, callback: () => void): void => {
  const codeMirrorEl: HTMLElementWithCodeMirror = el.siblings('.CodeMirror').get(0) as HTMLElement
  if (codeMirrorEl && codeMirrorEl.CodeMirror) {
    codeMirrorEl.CodeMirror.on(event, callback)
  }
  el.on(event, callback)
}


export interface HTMLElementWithCodeMirror extends HTMLElement {
  // eslint-disable-next-line
  CodeMirror?: any;
}


declare function Optimist(): string;


({ onClose }) => {
  const [escPressed, setEscPressed] = useState(false)

  useEffect(() => {
    if (escPressed) {
      onClose()
    }
  }, [escPressed, onClose])

  // escape handler must be constant
  const escapeHandler = useCallback<EscapeHandler>(
    evt => {
      evt.preventDefault()
      setEscPressed(true)
    },
    [setEscPressed]
  )

  useEscapeListenerStack(escapeHandler)

  return null
}


() => {
    if (escPressed) {
      onClose()
    }
  }


evt => {
      evt.preventDefault()
      setEscPressed(true)
    }


({ theme }) => theme.color.text01(opacity[48])


({
  children,
  ...props
}) => (
  <Layer>
    <ModalContainer {...props}>{children}</ModalContainer>
  </Layer>
)


({
  open,
  onClose,
  focusDialog = true,
  children,
  ...props
}) => {
  useEffect(() => {
    if (open) {
      noScroll.on()
      return () => noScroll.off()
    }
  }, [open])

  const id = useId('practical-modal')

  /**
   * Render
   */

  if (!open) {
    return null
  }

  return (
    <ModalBackdrop {...props}>
      {onClose !== undefined ? <CloseOnEscape onClose={onClose} /> : null}


() => {
    if (open) {
      noScroll.on()
      return () => noScroll.off()
    }
  }


() => noScroll.off()


interface ICloseOnEscapeProps {
  readonly onClose: VoidFunction
}


export interface ModalBackdropProps extends BaseProps {
  /**
   * `class` to be passed to the component.
   */
  readonly className?: BaseProps['className']
}


export interface IModalProps extends BaseProps {
  /**
   * `class` to be passed to the component.
   */
  readonly className?: BaseProps['className']
  /**
   * Handler which is called when the modal should be closed (e.g. when the user
   * pressed the escape key). You need to set open to `false` to close the modal,
   * or do nothing to leave the modal open.
   */
  readonly onClose?: VoidFunction
  /**
   * If `true`, centers the modal.
   */
  readonly verticallyCenter?: boolean
  /**
   * If `true`, puts the modal dialog into focus.
   */
  readonly focusDialog?: boolean
  /**
   * When `true` the dialog is rendered, `false` removes the dialog.
   */
  readonly open: boolean
}


type BaseElement = HTMLDivElement


type BaseProps = React.HTMLAttributes<BaseElement>


export declare class TextInput extends InputBase {
    constructor(config: Config, form: Form, item: Item, app: App, platform: Platform, elementRef: ElementRef, renderer: Renderer, scrollView: Content, nav: NavController, ngControl: NgControl);
    _clearInput: boolean;
    placeholder: string;
    clearInput: any;
    value: any;
    type: any;
    disabled: any;
    mode: string;
    clearOnEdit: any;
    _nativeInput: NativeInput;
    _nextInput: NextInput;
    blur: EventEmitter<Event>;
    focus: EventEmitter<Event>;
    inputBlurred(ev: UIEvent): void;
    inputFocused(ev: UIEvent): void;
    ngOnInit(): void;
    ngAfterContentChecked(): void;
    ngOnDestroy(): void;
    clearTextInput(): void;
}


export declare class TextArea extends InputBase {
    constructor(config: Config, form: Form, item: Item, app: App, platform: Platform, elementRef: ElementRef, renderer: Renderer, scrollView: Content, nav: NavController, ngControl: NgControl);
    placeholder: string;
    value: any;
    disabled: any;
    mode: string;
    _nativeInput: NativeInput;
    _nextInput: NextInput;
    blur: EventEmitter<Event>;
    focus: EventEmitter<Event>;
    ngOnInit(): void;
    ngAfterContentChecked(): void;
    ngOnDestroy(): void;
    inputBlurred(ev: UIEvent): void;
    inputFocused(ev: UIEvent): void;
}


inputBlurred(ev: UIEvent): void;


inputFocused(ev: UIEvent): void;


ngAfterContentChecked(): void;


clearTextInput(): void;


async (req, jwtPayload, done) => {
  if (jwtPayload) {
    try {
      let userProfile;
      const dm = new DataManager(shared.pgPool);
      const { UserProfileModel } = dm;

      userProfile = await UserProfileModel.findByKeycloakId(jwtPayload.sub);
      if (!userProfile) {
        // create one
        userProfile = await UserProfileModel.create({
          keycloakId: jwtPayload.sub,
        });
      }

      const user = {
        accessFlags: assignUserAccessFlags(jwtPayload),
        name: jwtPayload.name,
        preferredUsername: jwtPayload.preferred_username,
        givenName: jwtPayload.given_name,
        familyName: jwtPayload.family_name,
        email: jwtPayload.email,
      };

      const profile: AuthenticatedUser = { ...userProfile, ...user };

      // The returned user will be made available via `req.user`
      return done(null, profile); // OK
    } catch (err) {
      const message = 'JWT verification error';
      logger.error(`${message}, err = ${err.message}`);
      return done(null, null); // FAIL
    }
  }

  // tslint:disable-next-line:no-shadowed-variable
  const err = new Error('Unable to authenticate');
  // err.code = 401;

  return done(err, false);
}


async app => {
  // app.use(session(sessionOptions));
  app.use(passport.initialize());
  app.use(passport.session());

  // We don't store any user information.
  passport.serializeUser((user, done) => {
    logger.info('serialize');
    done(null, {});
  });

  // We don't load any addtional user information.
  passport.deserializeUser((id, done) => {
    logger.info('deserialize');
    done(null, {});
  });

  const { certificate, algorithm } = await getJwtCertificate(config.get('sso:certsUrl'));
  const opts: any = {};
  opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
  opts.algorithms = [algorithm];
  opts.secretOrKey = certificate;
  opts.passReqToCallback = true;
  // For development purposes only ignore the expiration
  // time of tokens.
  if (config.get('environment') === 'development') {
    opts.ignoreExpiration = true;
  }

  const jwtStrategy = new JwtStrategy(opts, verify);

  passport.use(jwtStrategy);
}


(user, done) => {
    logger.info('serialize');
    done(null, {});
  }


(id, done) => {
    logger.info('deserialize');
    done(null, {});
  }


export interface AuthenticatedUser {
  id: number;
  givenName: string;
  familyName: string;
  name: string;
  preferredUsername: string;
  email: string;
  archived: boolean;
  accessFlags: string[];
  lastSeenAt: object,
}


declare function TimeOutline({ height, width, style, color, cssClasses, className, onClick }: {
    height?: number;
    width?: number;
    style?: {};
    color?: string;
    cssClasses?: string;
    className?: string;
    onClick?: () => any;
}): any;


() => {
  it('match snapshot', async () => {
    const wrapper = mount(
      <Provider
        store={mockStore({
          builder: {
            variable: {
              editorDrawerOpen: true,
              editVariable: variable,
            },
            page: pageParams,
          },
        })}
      >
        <Component applicationId={pageParams.applicationId} folderId={pageParams.folderId} onSave={jest.fn()} />
      </Provider>,
    );
    expect(wrapper).toMatchSnapshot();
  });
}


async () => {
    const wrapper = mount(
      <Provider
        store={mockStore({
          builder: {
            variable: {
              editorDrawerOpen: true,
              editVariable: variable,
            },
            page: pageParams,
          },
        })}
      >
        <Component applicationId={pageParams.applicationId} folderId={pageParams.folderId} onSave={jest.fn()} />
      </Provider>,
    );
    expect(wrapper).toMatchSnapshot();
  }


mockStore({
          builder: {
            variable: {
              editorDrawerOpen: true,
              editVariable: variable,
            },
            page: pageParams,
          },
        })


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

  constructor(private quizService : QuizService,private router : Router) { }

  ngOnInit() {
  }

  SignOut() {
    localStorage.clear();
    clearInterval(this.quizService.timer);
    this.router.navigate(['/register']);
  }

}


SignOut() {
    localStorage.clear();
    clearInterval(this.quizService.timer);
    this.router.navigate(['/register']);
  }


function fetchPage(page: number, pages: DbUser[][], getString: GetStringFunction, interaction: CommandInteraction) {
	if (page > pages.length - 1) page = pages.length - 1
	if (page < 0) page = 0
	const pageEmbed = new MessageEmbed({
		color: colors.neutral,
		author: { name: getString("moduleName") },
		title: getString("pageTitle"),
		footer: {
			text: getString("pagination.page", { number: page + 1, total: pages.length }, "global"),
			iconURL: ((interaction.member as GuildMember | null) ?? interaction.user).displayAvatarURL({ format: "png", dynamic: true }),
		},
	})
	for (let i = 0; i <= pages[page].length - 1; i++) {
		// Get the user if we ever decide to change that
		// const user = interaction.client.users.cache.get(pages[page][i].id)!
		if (pages[page][i].levels) {
			pageEmbed.addField(
				getString("level", {
					rank: i + 1 + page * 24,
					level: pages[page][i].levels!.level,
					xp: parseToNumberString(pages[page][i].levels!.totalXp, getString),
				}),
				`<@!${pages[page][i].id}>${pages[page][i].id === interaction.user.id ? ` - **${getString("youIndicator")}**` : ""}`,
				true,
			)
		} else {
			pageEmbed.addField(
				getString("unranked", { rank: i + 1 + page * 24 }),
				`<@!${pages[page][i].id}>${pages[page][i].id === interaction.user.id ? ` - **${getString("youIndicator")}**` : ""}`,
				true,
			)
		}
	}
	return pageEmbed
}


p => p.some(u => u.id === interaction.user.id)


u => u.id === interaction.user.id


async buttonInteraction => {
				const userDb: DbUser = await client.getUser(buttonInteraction.user.id)
				if (interaction.user.id !== buttonInteraction.user.id) {
					return await buttonInteraction.reply({
						content: getString("pagination.notYours", { command: `/${this.name}` }, "global", userDb.lang),
						ephemeral: true,
					})
				} else if (buttonInteraction.customId === "first") page = 0
				else if (buttonInteraction.customId === "last") page = pages.length - 1
				else if (buttonInteraction.customId === "previous") {
					page--
					if (page < 0) page = 0
				} else if (buttonInteraction.customId === "next") {
					page++
					if (page > pages.length - 1) page = pages.length - 1
				}
				pageEmbed = fetchPage(page, pages, getString, interaction)
				controlButtons = updateButtonColors(controlButtons, page, pages)
				await buttonInteraction.update({ embeds: [pageEmbed], components: [controlButtons] })
			}


async () => {
				controlButtons.components.forEach(button => button.setDisabled(true))
				await interaction.editReply({
					content: getString("pagination.timeOut", { command: `\`/${this.name}\`` }, "global"),
					components: [controlButtons],
				})
			}


button => button.setDisabled(true)


async execute(interaction, getString: GetStringFunction) {
		const allUsers = await db
				.collection<DbUser>("users")
				.find({}, { sort: { "levels.totalXp": -1, id: 1 } })
				.toArray(),
			inputMe = interaction.options.getBoolean("me", false),
			inputPage = interaction.options.getInteger("page", false),
			pages: DbUser[][] = [] // Inner arrays are of length 24
		let n = 0
		while (n < allUsers.length) pages.push(allUsers.slice(n, (n += 24))) // Max number of fields divisible by 3

		let page = 0
		if (inputMe) page = pages.indexOf(pages.find(p => p.some(u => u.id === interaction.user.id))!)
		else if (inputPage) page = inputPage - 1

		if (page >= pages.length || page < 0) {
			const embed = new MessageEmbed({
				color: colors.error,
				author: { name: getString("moduleName") },
				title: getString("pageTitle"),
				description: getString("pageNotExist"),
				footer: {
					text: generateTip(getString),
					iconURL: ((interaction.member as GuildMember | null) ?? interaction.user).displayAvatarURL({ format: "png", dynamic: true }),
				},
			})
			return await interaction.reply({ embeds: [embed] })
		} else {
			let controlButtons = new MessageActionRow({
					components: [
						new MessageButton({
							style: "SUCCESS",
							emoji: "⏮",
							customId: "first",
							label: getString("pagination.first", "global"),
						}),
						new MessageButton({
							style: "SUCCESS",
							emoji: "◀",
							customId: "previous",
							label: getString("pagination.previous", "global"),
						}),
						new MessageButton({
							style: "SUCCESS",
							emoji: "▶",
							customId: "next",
							label: getString("pagination.next", "global"),
						}),
						new MessageButton({
							style: "SUCCESS",
							emoji: "⏭",
							customId: "last",
							label: getString("pagination.last", "global"),
						}),
					],
				}),
				pageEmbed = fetchPage(page, pages, getString, interaction)

			controlButtons = updateButtonColors(controlButtons, page, pages)
			const msg = (await interaction.reply({ embeds: [pageEmbed], components: [controlButtons], fetchReply: true })) as Message,
				collector = msg.createMessageComponentCollector<"BUTTON">({ idle: this.cooldown! * 1000 })

			collector.on("collect", async buttonInteraction => {
				const userDb: DbUser = await client.getUser(buttonInteraction.user.id)
				if (interaction.user.id !== buttonInteraction.user.id) {
					return await buttonInteraction.reply({
						content: getString("pagination.notYours", { command: `/${this.name}` }, "global", userDb.lang),
						ephemeral: true,
					})
				} else if (buttonInteraction.customId === "first") page = 0
				else if (buttonInteraction.customId === "last") page = pages.length - 1
				else if (buttonInteraction.customId === "previous") {
					page--
					if (page < 0) page = 0
				} else if (buttonInteraction.customId === "next") {
					page++
					if (page > pages.length - 1) page = pages.length - 1
				}
				pageEmbed = fetchPage(page, pages, getString, interaction)
				controlButtons = updateButtonColors(controlButtons, page, pages)
				await buttonInteraction.update({ embeds: [pageEmbed], components: [controlButtons] })
			})

			collector.on("end", async () => {
				controlButtons.components.forEach(button => button.setDisabled(true))
				await interaction.editReply({
					content: getString("pagination.timeOut", { command: `\`/${this.name}\`` }, "global"),
					components: [controlButtons],
				})
			})
		}
	}


(): DataLoader<number, typeof models.EmojiReaction> => {
    return new DataLoader(async updateIds => {
      const reactionsList = await models.EmojiReaction.count({
        where: { UpdateId: { [Op.in]: updateIds } },
        group: ['UpdateId', 'emoji'],
        order: [['emoji', 'ASC']],
        raw: true,
      });

      const reactionsMap = {};
      reactionsList.forEach(({ UpdateId, emoji, count }) => {
        set(reactionsMap, [UpdateId, emoji], count);
      });

      return updateIds.map(id => reactionsMap[id] || {});
    });
  }


async updateIds => {
      const reactionsList = await models.EmojiReaction.count({
        where: { UpdateId: { [Op.in]: updateIds } },
        group: ['UpdateId', 'emoji'],
        order: [['emoji', 'ASC']],
        raw: true,
      });

      const reactionsMap = {};
      reactionsList.forEach(({ UpdateId, emoji, count }) => {
        set(reactionsMap, [UpdateId, emoji], count);
      });

      return updateIds.map(id => reactionsMap[id] || {});
    }


({ UpdateId, emoji, count }) => {
        set(reactionsMap, [UpdateId, emoji], count);
      }


id => reactionsMap[id] || {}


(req: express.Request): DataLoader<number, typeof models.EmojiReaction> => {
    return new DataLoader(async updateIds => {
      if (!req.remoteUser) {
        return updateIds.map(() => []);
      }

      const reactionsList = await models.EmojiReaction.findAll({
        attributes: ['UpdateId', [sequelize.fn('ARRAY_AGG', sequelize.col('emoji')), 'emojis']],
        where: { FromCollectiveId: req.remoteUser.CollectiveId, UpdateId: { [Op.in]: updateIds } },
        group: ['UpdateId'],
        raw: true,
      });

      const reactionsMap = {};
      reactionsList.forEach(reaction => {
        reactionsMap[reaction.UpdateId] = reaction.emojis;
      });

      return updateIds.map(id => reactionsMap[id] || []);
    });
  }


async updateIds => {
      if (!req.remoteUser) {
        return updateIds.map(() => []);
      }

      const reactionsList = await models.EmojiReaction.findAll({
        attributes: ['UpdateId', [sequelize.fn('ARRAY_AGG', sequelize.col('emoji')), 'emojis']],
        where: { FromCollectiveId: req.remoteUser.CollectiveId, UpdateId: { [Op.in]: updateIds } },
        group: ['UpdateId'],
        raw: true,
      });

      const reactionsMap = {};
      reactionsList.forEach(reaction => {
        reactionsMap[reaction.UpdateId] = reaction.emojis;
      });

      return updateIds.map(id => reactionsMap[id] || []);
    }


reaction => {
        reactionsMap[reaction.UpdateId] = reaction.emojis;
      }


id => reactionsMap[id] || []


export function alert(msg: string): void;


function getBufferKey(byteSize: number, usage: GPUBufferUsage) {
  return `${byteSize}_${usage}`;
}


(buffers, key) => {
      buffers.forEach(buff => {
        buff.destroy();
      });
    }


buff => {
        buff.destroy();
      }


/**
 * @license
 * Copyright 2019 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */
export class BufferManager {
  private numUsedBuffers = 0;
  private numFreeBuffers = 0;
  private freeBuffers: Map<string, GPUBuffer[]> = new Map();
  private usedBuffers: Map<string, GPUBuffer[]> = new Map();

  public numBytesUsed = 0;

  constructor(private device: GPUDevice) {}

  acquireBuffer(byteSize: number, usage: GPUBufferUsage) {
    const key = getBufferKey(byteSize, usage);
    if (!this.freeBuffers.has(key)) {
      this.freeBuffers.set(key, []);
    }

    if (!this.usedBuffers.has(key)) {
      this.usedBuffers.set(key, []);
    }

    this.numBytesUsed += byteSize;
    this.numUsedBuffers++;

    if (this.freeBuffers.get(key).length > 0) {
      this.numFreeBuffers--;

      const newBuffer = this.freeBuffers.get(key).shift();
      this.usedBuffers.get(key).push(newBuffer);
      return newBuffer;
    }

    const newBuffer = this.device.createBuffer({size: byteSize, usage});
    this.usedBuffers.get(key).push(newBuffer);

    return newBuffer;
  }

  releaseBuffer(buffer: GPUBuffer, byteSize: number, usage: GPUBufferUsage) {
    if (this.freeBuffers == null) {
      return;
    }

    const key = getBufferKey(byteSize, usage);
    if (!this.freeBuffers.has(key)) {
      this.freeBuffers.set(key, []);
    }

    this.freeBuffers.get(key).push(buffer);
    this.numFreeBuffers++;
    this.numUsedBuffers--;

    const bufferList = this.usedBuffers.get(key);
    const bufferIndex = bufferList.indexOf(buffer);
    if (bufferIndex < 0) {
      throw new Error(
          'Cannot release a buffer that was never provided by this ' +
          'buffer manager');
    }
    bufferList.splice(bufferIndex, 1);
    this.numBytesUsed -= byteSize;
  }

  getNumUsedBuffers(): number {
    return this.numUsedBuffers;
  }

  getNumFreeBuffers(): number {
    return this.numFreeBuffers;
  }

  reset() {
    this.freeBuffers = new Map();
    this.usedBuffers = new Map();
    this.numUsedBuffers = 0;
    this.numFreeBuffers = 0;
  }

  dispose() {
    if (this.freeBuffers == null && this.usedBuffers == null) {
      return;
    }

    this.freeBuffers.forEach((buffers, key) => {
      buffers.forEach(buff => {
        buff.destroy();
      });
    });

    this.usedBuffers.forEach((buffers, key) => {
      buffers.forEach(buff => {
        buff.destroy();
      });
    });

    this.freeBuffers = null;
    this.usedBuffers = null;
    this.numUsedBuffers = 0;
    this.numFreeBuffers = 0;
  }
}


acquireBuffer(byteSize: number, usage: GPUBufferUsage) {
    const key = getBufferKey(byteSize, usage);
    if (!this.freeBuffers.has(key)) {
      this.freeBuffers.set(key, []);
    }

    if (!this.usedBuffers.has(key)) {
      this.usedBuffers.set(key, []);
    }

    this.numBytesUsed += byteSize;
    this.numUsedBuffers++;

    if (this.freeBuffers.get(key).length > 0) {
      this.numFreeBuffers--;

      const newBuffer = this.freeBuffers.get(key).shift();
      this.usedBuffers.get(key).push(newBuffer);
      return newBuffer;
    }

    const newBuffer = this.device.createBuffer({size: byteSize, usage});
    this.usedBuffers.get(key).push(newBuffer);

    return newBuffer;
  }


releaseBuffer(buffer: GPUBuffer, byteSize: number, usage: GPUBufferUsage) {
    if (this.freeBuffers == null) {
      return;
    }

    const key = getBufferKey(byteSize, usage);
    if (!this.freeBuffers.has(key)) {
      this.freeBuffers.set(key, []);
    }

    this.freeBuffers.get(key).push(buffer);
    this.numFreeBuffers++;
    this.numUsedBuffers--;

    const bufferList = this.usedBuffers.get(key);
    const bufferIndex = bufferList.indexOf(buffer);
    if (bufferIndex < 0) {
      throw new Error(
          'Cannot release a buffer that was never provided by this ' +
          'buffer manager');
    }
    bufferList.splice(bufferIndex, 1);
    this.numBytesUsed -= byteSize;
  }


getNumUsedBuffers(): number {
    return this.numUsedBuffers;
  }


getNumFreeBuffers(): number {
    return this.numFreeBuffers;
  }


reset() {
    this.freeBuffers = new Map();
    this.usedBuffers = new Map();
    this.numUsedBuffers = 0;
    this.numFreeBuffers = 0;
  }


dispose() {
    if (this.freeBuffers == null && this.usedBuffers == null) {
      return;
    }

    this.freeBuffers.forEach((buffers, key) => {
      buffers.forEach(buff => {
        buff.destroy();
      });
    });

    this.usedBuffers.forEach((buffers, key) => {
      buffers.forEach(buff => {
        buff.destroy();
      });
    });

    this.freeBuffers = null;
    this.usedBuffers = null;
    this.numUsedBuffers = 0;
    this.numFreeBuffers = 0;
  }


export function validate(input: Validatable) {
  let isValid = true;
  let inputLength = input.value.toString().trim().length;
  if (input.required) {
    isValid = isValid && input.value.toString().trim().length !== 0;
  }
  if (input.minLength != null && typeof input.value === "string") {
    isValid = isValid && inputLength >= input.minLength;
  }
  if (input.maxLength != null && typeof input.value === "string") {
    isValid = isValid && inputLength <= input.maxLength;
  }
  if (input.min != null && typeof input.value === "number") {
    isValid = isValid && input.value >= input.min;
  }
  if (input.max != null && typeof input.value === "number") {
    isValid = isValid && input.value <= input.max;
  }

  return isValid;
}


export interface Validatable {
  value: string | number;
  required?: boolean;
  minLength?: number;
  maxLength?: number;
  min?: number;
  max?: number;
}


function getPluginUrl(pluginName: string, ext = '.js') {
  return `${baseUrl}/plugins/${pluginName}/index${ext}`
}


function getPluginFilePath(pluginName: string, ext = '.js') {
  return join(BASE_PLUGIN_DIR, pluginName + ext)
}


async function downloadPlugin(plugin: ToolPluginBase) {
  const url = getPluginUrl(plugin.plugin)
  const data = await request.get(url)
  await fsp.writeFile(getPluginFilePath(plugin.plugin), data.text || data.body)
  if (plugin.css) {
    const cssUrl = getPluginUrl(plugin.plugin, '.css')
    const cssData = await request.get(cssUrl)
    await fsp.writeFile(
      getPluginFilePath(plugin.plugin, '.css'),
      cssData.text || cssData.body
    )
  }
}


async function removePlugin(plugin: ToolPluginBase) {
  await fsp.rm(getPluginFilePath(plugin.plugin))
}


function bind(): void {
  fsp.mkdir(BASE_PLUGIN_DIR, { recursive: true })
  ipcMain.handle(channel, async (e, type, data) => {
    try {
      if (data.isDev) return {}
      if (type === 'download' || type === 'update') {
        await downloadPlugin(data)
        return {}
      } else if (type === 'remove') {
        await removePlugin(data)
        return {}
      } else if (type === 'getPath') {
        return getPluginFilePath(data.plugin, data.ext || '.js')
      }
    } catch (error) {
      return { error }
    }
  })
}


function unbind(): void {
  ipcMain.removeHandler(channel)
  console.log(`${channel} 结束监听`)
}


async (e, type, data) => {
    try {
      if (data.isDev) return {}
      if (type === 'download' || type === 'update') {
        await downloadPlugin(data)
        return {}
      } else if (type === 'remove') {
        await removePlugin(data)
        return {}
      } else if (type === 'getPath') {
        return getPluginFilePath(data.plugin, data.ext || '.js')
      }
    } catch (error) {
      return { error }
    }
  }


/* tslint:disable */
/* eslint-disable */
/**
 * Jellyfin API
 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 *
 * The version of the OpenAPI document: 10.7.6
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */
/**
 * Set channel mapping dto.
 * @export
 * @interface SetChannelMappingDto
 */
export interface SetChannelMappingDto {
    /**
     * Gets or sets the provider id.
     * @type {string}
     * @memberof SetChannelMappingDto
     */
    ProviderId: string;
    /**
     * Gets or sets the tuner channel id.
     * @type {string}
     * @memberof SetChannelMappingDto
     */
    TunerChannelId: string;
    /**
     * Gets or sets the provider channel id.
     * @type {string}
     * @memberof SetChannelMappingDto
     */
    ProviderChannelId: string;
}


function fromIndex(i: number): Step {
  if (i < 0 || i >= 8) throw new Error(`Invalid step index: ${i}`)

  const x = (i >> 0) & 1 ? 1 : 0
  const y = (i >> 1) & 1 ? 1 : 0
  const z = (i >> 2) & 1 ? 1 : 0
  return [x, y, z]
}


function list(): Step[] {
  return [
    [0, 0, 0],
    [0, 0, 1],
    [0, 1, 0],
    [0, 1, 1],
    [1, 0, 0],
    [1, 0, 1],
    [1, 1, 0],
    [1, 1, 1],
  ]
}


export type Step = [0 | 1, 0 | 1, 0 | 1]


export function sum(x: number, y: number): number {
    return x + y;
}


function Example() {
  const [showDialog, setShowDialog] = React.useState(false);
  const transitions = useTransition<any, any>(showDialog, null, {
    from: { opacity: 0, y: -10 },
    enter: { opacity: 1, y: 0 },
    leave: { opacity: 0, y: 10 },
    config: config.stiff,
  });
  return (
    <div>
      <button onClick={() => setShowDialog(true)}


({ item, props: styles }) =>
          item && (
            <AnimatedDialogOverlay
              key


(value: any) => `translate3d(0px, ${value}px, 0px)`


() => setShowDialog(false)


(props: any) => {
  const [selected, setSelected] = React.useState<string | number>(0);

  return (
    <L.Div _inner>
      <L.Tabs
        activeTabKey={selected}
        onChange={(ev)


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

  constructor() { }

  ngOnInit(): void {
  }

}


(result: Text) => {
            this.text = result;
            this.text.textParts = this.processTextParts(result.textParts);

            this.languageDictionary = this.text.languageDictionary;
            this.setTranslateLink('', this.languageDictionary);
            this.changeDetectorRef.detectChanges();
        }


() => {
            const editedTextParts: TextPart[] = [];

            for (const textPart of this.text.textParts) {
                if (textPart.type === 'word' && !!textPart.word && textPart.word._id === word._id) {
                    editedTextParts.push({
                        content: textPart.content,
                        type: 'word',
                        word,
                        color: getColor(word.level),
                        title: word.translation,
                    });
                } else {
                    editedTextParts.push(JSON.parse(JSON.stringify(textPart)));
                }
            }

            this.text.textParts = editedTextParts;
            this.text.percentageUnknown = Text.getPercentageUnknown(this.text);
            this.text.percentageLearning = Text.getPercentageLearning(this.text);

            this.changeDetectorRef.detectChanges();
        }


(selectionIndex: number) => firstWords.push(firstWordIndexes.indexOf(selectionIndex))


(textPart: TextPart) =>
            textPart.content.toLowerCase() === firstWord.toLowerCase()


(firstWordIndex: number) => {
            const wordObject = firstWordParts[firstWordIndex];
            if (!wordObject.selections || wordObject.selections.length === 0) {
                wordObject.selections = [];
            }

            wordObject.selections.push({
                content: selection,
                translation: this.selectionForm.get('translation').value,
                level: 0.1,
            } as Word);
        }


(textPart: TextPart) => ({
            ...textPart,
            color: textPart.type === 'word' && !!textPart.word ? getColor(textPart.word.level) : '',
            title: textPart.type === 'word' && !!textPart.word ? textPart.word.translation || '' : ''
        })


(node: Node) => {
                if (node.nodeName === 'SPAN') {
                    let nodeContent = node.textContent;
                    if (nodeContent[0] === ' ' && nodeContent[nodeContent.length - 1] === ' ') {
                        nodeContent = nodeContent.substring(1, nodeContent.length - 1);
                    }
                    selectionParts.push(nodeContent);
                }
            }


@Component({
    selector: 'app-read-text',
    templateUrl: './read-text.component.html',
    styleUrls: ['./read-text.component.scss'],
    providers: [ClickService, TextService, LanguageService, WordService, TextArchiveService],
})
export class ReadTextComponent implements OnInit {
    public text: Text;
    public translateLink: string = '';
    public percentage: number = 0;

    @ViewChild('translationField')
    public translationField: ElementRef<HTMLInputElement>;

    public selectionPopupShowed: boolean = false;
    public selectionForm: FormGroup;

    private languageDictionary: string;

    constructor(
        private readonly formBuilder: FormBuilder,
        private readonly route: ActivatedRoute,
        private readonly clickService: ClickService,
        private readonly languageService: LanguageService,
        private readonly textService: TextService,
        private readonly textArchiveService: TextArchiveService,
        private readonly wordService: WordService,
        private readonly changeDetectorRef: ChangeDetectorRef,
    ) {
    }

    ngOnInit() {
        const textId = this.route.snapshot.params.id;

        this.textService.getParsed(textId).subscribe((result: Text) => {
            this.text = result;
            this.text.textParts = this.processTextParts(result.textParts);

            this.languageDictionary = this.text.languageDictionary;
            this.setTranslateLink('', this.languageDictionary);
            this.changeDetectorRef.detectChanges();
        });
    }

    public setTranslateLink(word: string, dictionary: string = this.languageDictionary): void {
        this.translateLink = dictionary.replace('{word}', word);
        this.changeDetectorRef.detectChanges();
    }

    public wordEdit(word: Word): void {
        this.textService.editWord(word).subscribe(() => {
            const editedTextParts: TextPart[] = [];

            for (const textPart of this.text.textParts) {
                if (textPart.type === 'word' && !!textPart.word && textPart.word._id === word._id) {
                    editedTextParts.push({
                        content: textPart.content,
                        type: 'word',
                        word,
                        color: getColor(word.level),
                        title: word.translation,
                    });
                } else {
                    editedTextParts.push(JSON.parse(JSON.stringify(textPart)));
                }
            }

            this.text.textParts = editedTextParts;
            this.text.percentageUnknown = Text.getPercentageUnknown(this.text);
            this.text.percentageLearning = Text.getPercentageLearning(this.text);

            this.changeDetectorRef.detectChanges();
        });
    }

    public onTextAreaClick(): void {
        const selection = window.getSelection();

        if (selection.type === 'Range') {
            this.processSelection(selection);
        }

        this.clickService.click();
    }

    public onLoaded(payload: { percentage: number }): void {
        this.percentage = payload.percentage;
        this.changeDetectorRef.detectChanges();
    }

    public archive(): void {
        this.textArchiveService.archiveText(this.text._id).subscribe();
    }

    public saveSelection(): void {
        if (this.selectionForm.invalid) {
            return;
        }

        console.dir(this.selectionForm.value);
        const selection = this.selectionForm.get('selection').value;

        this.wordService.saveSelection({
            content: selection,
            translation: this.selectionForm.get('translation').value,
            level: 0.1,
        } as Word).subscribe();

        // find selection indices
        const selectionIndexes = this.indexes(this.text.text, selection);
        // get the first word
        const firstWord = selection.split(' ')[0];
        // find indexes of the first words
        const firstWordIndexes = this.indexes(this.text.text, firstWord);
        // create a list of first words indexes in selection indexes tables
        const firstWords: number[] = [];
        selectionIndexes.forEach((selectionIndex: number) => firstWords.push(firstWordIndexes.indexOf(selectionIndex)));

        // filter in text.textParts first words
        const firstWordParts = this.text.textParts.filter((textPart: TextPart) =>
            textPart.content.toLowerCase() === firstWord.toLowerCase());
        // for every firstWords index take a first word
        firstWords.forEach((firstWordIndex: number) => {
            const wordObject = firstWordParts[firstWordIndex];
            if (!wordObject.selections || wordObject.selections.length === 0) {
                wordObject.selections = [];
            }

            wordObject.selections.push({
                content: selection,
                translation: this.selectionForm.get('translation').value,
                level: 0.1,
            } as Word);
        });
        // deep copy text parts with added selections
        this.text.textParts = JSON.parse(JSON.stringify(this.text.textParts));

        this.selectionPopupShowed = false;
        this.changeDetectorRef.detectChanges();
    }

    private indexes(source: string, find: string): number[] {
        if (!source) {
            return [];
        }
        // if find is empty string return all indexes.
        // if (!find) {
        //     // or shorter arrow function:
        //     // return source.split('').map((_,i) => i);
        //     return source.split('').map(function(_, i) { return i; });
        // }
        const result = [];
        for (let i = 0; i < source.length; ++i) {
            if (source.substring(i, i + find.length).toLowerCase() === find.toLowerCase()) {
                result.push(i);
            }
        }
        return result;
    }

    private processTextParts(textParts: TextPart[]): TextPart[] {
        return textParts.map((textPart: TextPart) => ({
            ...textPart,
            color: textPart.type === 'word' && !!textPart.word ? getColor(textPart.word.level) : '',
            title: textPart.type === 'word' && !!textPart.word ? textPart.word.translation || '' : ''
        }));
    }

    private processSelection(selection: Selection): void {
        // get the first app-word node
        let firstNode: Node = selection.anchorNode;
        while (firstNode.nodeName !== 'APP-WORD') {
            firstNode = firstNode.parentElement;
        }

        // get the last text
        const lastText = selection.extentNode.textContent.trim();

        const selectionParts: string[] = [];
        let currentNode: Node = firstNode;

        do {
            // get the text on the app-word node
            currentNode.childNodes.forEach((node: Node) => {
                if (node.nodeName === 'SPAN') {
                    let nodeContent = node.textContent;
                    if (nodeContent[0] === ' ' && nodeContent[nodeContent.length - 1] === ' ') {
                        nodeContent = nodeContent.substring(1, nodeContent.length - 1);
                    }
                    selectionParts.push(nodeContent);
                }
            });
            currentNode = currentNode.nextSibling;
        } while (selectionParts.length === 0 || selectionParts[selectionParts.length - 1].trim() !== lastText);
        const selectionText = selectionParts.join('');
        this.setTranslateLink(selectionText);

        this.selectionForm = this.formBuilder.group({
            selection: selectionText,
            translation: this.formBuilder.control('', Validators.required),
        });

        this.selectionPopupShowed = true;
        this.changeDetectorRef.detectChanges();
    }
}


ngOnInit() {
        const textId = this.route.snapshot.params.id;

        this.textService.getParsed(textId).subscribe((result: Text) => {
            this.text = result;
            this.text.textParts = this.processTextParts(result.textParts);

            this.languageDictionary = this.text.languageDictionary;
            this.setTranslateLink('', this.languageDictionary);
            this.changeDetectorRef.detectChanges();
        });
    }


public setTranslateLink(word: string, dictionary: string = this.languageDictionary): void {
        this.translateLink = dictionary.replace('{word}', word);
        this.changeDetectorRef.detectChanges();
    }


public wordEdit(word: Word): void {
        this.textService.editWord(word).subscribe(() => {
            const editedTextParts: TextPart[] = [];

            for (const textPart of this.text.textParts) {
                if (textPart.type === 'word' && !!textPart.word && textPart.word._id === word._id) {
                    editedTextParts.push({
                        content: textPart.content,
                        type: 'word',
                        word,
                        color: getColor(word.level),
                        title: word.translation,
                    });
                } else {
                    editedTextParts.push(JSON.parse(JSON.stringify(textPart)));
                }
            }

            this.text.textParts = editedTextParts;
            this.text.percentageUnknown = Text.getPercentageUnknown(this.text);
            this.text.percentageLearning = Text.getPercentageLearning(this.text);

            this.changeDetectorRef.detectChanges();
        });
    }


public onTextAreaClick(): void {
        const selection = window.getSelection();

        if (selection.type === 'Range') {
            this.processSelection(selection);
        }

        this.clickService.click();
    }


public onLoaded(payload: { percentage: number }): void {
        this.percentage = payload.percentage;
        this.changeDetectorRef.detectChanges();
    }


public archive(): void {
        this.textArchiveService.archiveText(this.text._id).subscribe();
    }


public saveSelection(): void {
        if (this.selectionForm.invalid) {
            return;
        }

        console.dir(this.selectionForm.value);
        const selection = this.selectionForm.get('selection').value;

        this.wordService.saveSelection({
            content: selection,
            translation: this.selectionForm.get('translation').value,
            level: 0.1,
        } as Word).subscribe();

        // find selection indices
        const selectionIndexes = this.indexes(this.text.text, selection);
        // get the first word
        const firstWord = selection.split(' ')[0];
        // find indexes of the first words
        const firstWordIndexes = this.indexes(this.text.text, firstWord);
        // create a list of first words indexes in selection indexes tables
        const firstWords: number[] = [];
        selectionIndexes.forEach((selectionIndex: number) => firstWords.push(firstWordIndexes.indexOf(selectionIndex)));

        // filter in text.textParts first words
        const firstWordParts = this.text.textParts.filter((textPart: TextPart) =>
            textPart.content.toLowerCase() === firstWord.toLowerCase());
        // for every firstWords index take a first word
        firstWords.forEach((firstWordIndex: number) => {
            const wordObject = firstWordParts[firstWordIndex];
            if (!wordObject.selections || wordObject.selections.length === 0) {
                wordObject.selections = [];
            }

            wordObject.selections.push({
                content: selection,
                translation: this.selectionForm.get('translation').value,
                level: 0.1,
            } as Word);
        });
        // deep copy text parts with added selections
        this.text.textParts = JSON.parse(JSON.stringify(this.text.textParts));

        this.selectionPopupShowed = false;
        this.changeDetectorRef.detectChanges();
    }


private indexes(source: string, find: string): number[] {
        if (!source) {
            return [];
        }
        // if find is empty string return all indexes.
        // if (!find) {
        //     // or shorter arrow function:
        //     // return source.split('').map((_,i) => i);
        //     return source.split('').map(function(_, i) { return i; });
        // }
        const result = [];
        for (let i = 0; i < source.length; ++i) {
            if (source.substring(i, i + find.length).toLowerCase() === find.toLowerCase()) {
                result.push(i);
            }
        }
        return result;
    }


private processTextParts(textParts: TextPart[]): TextPart[] {
        return textParts.map((textPart: TextPart) => ({
            ...textPart,
            color: textPart.type === 'word' && !!textPart.word ? getColor(textPart.word.level) : '',
            title: textPart.type === 'word' && !!textPart.word ? textPart.word.translation || '' : ''
        }));
    }


private processSelection(selection: Selection): void {
        // get the first app-word node
        let firstNode: Node = selection.anchorNode;
        while (firstNode.nodeName !== 'APP-WORD') {
            firstNode = firstNode.parentElement;
        }

        // get the last text
        const lastText = selection.extentNode.textContent.trim();

        const selectionParts: string[] = [];
        let currentNode: Node = firstNode;

        do {
            // get the text on the app-word node
            currentNode.childNodes.forEach((node: Node) => {
                if (node.nodeName === 'SPAN') {
                    let nodeContent = node.textContent;
                    if (nodeContent[0] === ' ' && nodeContent[nodeContent.length - 1] === ' ') {
                        nodeContent = nodeContent.substring(1, nodeContent.length - 1);
                    }
                    selectionParts.push(nodeContent);
                }
            });
            currentNode = currentNode.nextSibling;
        } while (selectionParts.length === 0 || selectionParts[selectionParts.length - 1].trim() !== lastText);
        const selectionText = selectionParts.join('');
        this.setTranslateLink(selectionText);

        this.selectionForm = this.formBuilder.group({
            selection: selectionText,
            translation: this.formBuilder.control('', Validators.required),
        });

        this.selectionPopupShowed = true;
        this.changeDetectorRef.detectChanges();
    }


export default class DemoFieldCustomizer
  extends BaseFieldCustomizer<IDemoFieldCustomizerProperties> {

  @override
  public onInit(): Promise<void> {
    // Add your custom initialization to this method.  The framework will wait
    // for the returned promise to resolve before firing any BaseFieldCustomizer events.
    Log.info(LOG_SOURCE, 'Activated DemoFieldCustomizer with properties:');
    Log.info(LOG_SOURCE, JSON.stringify(this.properties, undefined, 2));
    Log.info(LOG_SOURCE, `The following string should be equal: "DemoFieldCustomizer" and "${strings.Title}"`);
    return Promise.resolve();
  }

  @override
  public onRenderCell(event: IFieldCustomizerCellEventParameters): void {
    // Use this method to perform your custom cell rendering.
    const text: string = event.fieldValue.toString();

    const demo: React.ReactElement<{}> =
      React.createElement(Demo, { text } as IDemoProps);

    ReactDOM.render(demo, event.domElement);
  }

  @override
  public onDisposeCell(event: IFieldCustomizerCellEventParameters): void {
    // This method should be used to free any resources that were allocated during rendering.
    // For example, if your onRenderCell() called ReactDOM.render(), then you should
    // call ReactDOM.unmountComponentAtNode() here.
    ReactDOM.unmountComponentAtNode(event.domElement);
    super.onDisposeCell(event);
  }
}


/**
 * If your field customizer uses the ClientSideComponentProperties JSON input,
 * it will be deserialized into the BaseExtension.properties object.
 * You can define an interface to describe it.
 */
export interface IDemoFieldCustomizerProperties {
  // This is an example; replace with your own property
  sampleText?: string;
}


@override
  public onInit(): Promise<void> {
    // Add your custom initialization to this method.  The framework will wait
    // for the returned promise to resolve before firing any BaseFieldCustomizer events.
    Log.info(LOG_SOURCE, 'Activated DemoFieldCustomizer with properties:');
    Log.info(LOG_SOURCE, JSON.stringify(this.properties, undefined, 2));
    Log.info(LOG_SOURCE, `The following string should be equal: "DemoFieldCustomizer" and "${strings.Title}"`);
    return Promise.resolve();
  }


@override
  public onRenderCell(event: IFieldCustomizerCellEventParameters): void {
    // Use this method to perform your custom cell rendering.
    const text: string = event.fieldValue.toString();

    const demo: React.ReactElement<{}> =
      React.createElement(Demo, { text } as IDemoProps);

    ReactDOM.render(demo, event.domElement);
  }


@override
  public onDisposeCell(event: IFieldCustomizerCellEventParameters): void {
    // This method should be used to free any resources that were allocated during rendering.
    // For example, if your onRenderCell() called ReactDOM.render(), then you should
    // call ReactDOM.unmountComponentAtNode() here.
    ReactDOM.unmountComponentAtNode(event.domElement);
    super.onDisposeCell(event);
  }


(_, index) => (
                    <ConnectionsGridCell key={index}>
                      <ConnectionSkeleton />
                    </ConnectionsGridCell>


(s, index) => {
                  return (
                    <ConnectionsGridCell key={index}>
                      <ConnectionCard
                        name={s.name}
                        description={s.description || ''}
                        icon={<EntityIcon entity={s} alt={s.name} width={46} />}


export class EditorSteps extends React.Component<IEditorStepsProps> {
  public render() {
    return (
      <Translation ns={['connections', 'shared']}>
        {t => (
          <ConnectionsGrid>
            <WithLoader
              error={this.props.error}
              loading={this.props.loading}
              loaderChildren={
                <>
                  {new Array(5).fill(0).map((_, index) => (
                    <ConnectionsGridCell key={index}>
                      <ConnectionSkeleton />
                    </ConnectionsGridCell>
                  ))}


export interface IEditorStepsProps {
  error: boolean;
  loading: boolean;
  steps: IUIStep[];

  getEditorStepHref(editorStep: IUIStep): H.LocationDescriptor;
}


public render() {
    return (
      <Translation ns={['connections', 'shared']}>
        {t => (
          <ConnectionsGrid>
            <WithLoader
              error={this.props.error}
              loading={this.props.loading}
              loaderChildren={
                <>
                  {new Array(5).fill(0).map((_, index) => (
                    <ConnectionsGridCell key={index}>
                      <ConnectionSkeleton />
                    </ConnectionsGridCell>
                  ))}


t('cannotDelete')


t('configurationRequired')


t('techPreview')


/**
 * `AnchorsComponent` for the `PolylineRegion` class.
 */
export declare class AnchorsElement extends AnchorsComponent {
    /**
     * Default threshold distance to define whether ctrl-pointer click is on point or line.
     */
    static ANCHOR_POINT_LINE_SWITCH_THRESHOLD: number;
    /**
     * Internal flag to delete a point on pointer up event.
     */
    private deleteOnPointerUp;
    /**
     * Internal flat to add a point on pointer up event.
     */
    private addOnPointerUp;
    /**
     * Current number of anchors.
     */
    private anchorsLength;
    /**
     * Reference to the polyline object, used to trigger adding/deleting points.
     */
    private anchorsPolyline;
    /**
     * Creates a new `AnchorsElement` object.
     * @param paper - The `Snap.Paper` object to draw on.
     * @param paperRect - The parent bounding box for created component.
     * @param regionData - The `RegionData` object shared across components. Used also for initial setup.
     * @param callbacks - The external callbacks collection.
     */
    constructor(paper: Snap.Paper, paperRect: Rect, regionData: RegionData, callbacks: IRegionCallbacks);
    /**
     * Redraws the componnent.
     */
    redraw(): void;
    /**
     * Creates a collection on anchors.
     */
    protected buildAnchors(): void;
    /**
     * Creates acollection of anchor points.
     */
    protected buildPolylineAnchors(): void;
    /**
     * Subscribe an anchor to events.
     * @param anchor - The anchor to wire up with events.
     */
    protected subscribeLineToEvents(anchor: Snap.Element): void;
    /**
     * Updated the `regionData` based on the new ghost anchor location. Should be redefined in child classes.
     * @param p - The new ghost anchor location.
     */
    protected updateRegion(p: Point2D): void;
    /**
     * Callback for the pointerenter event for the ghost anchor.
     * @param e - PointerEvent object.
     */
    protected onGhostPointerEnter(e: PointerEvent): void;
    /**
     * Callback for the pointermove event for the ghost anchor.
     * @param e - PointerEvent object.
     */
    protected onGhostPointerMove(e: PointerEvent): void;
    /**
     * Callback for the pointerup event for the ghost anchor.
     * @param e - PointerEvent object.
     */
    protected onGhostPointerUp(e: PointerEvent): void;
}


/**
     * Redraws the componnent.
     */
redraw(): void;


/**
     * Creates a collection on anchors.
     */
protected buildAnchors(): void;


/**
     * Creates acollection of anchor points.
     */
protected buildPolylineAnchors(): void;


/**
     * Subscribe an anchor to events.
     * @param anchor - The anchor to wire up with events.
     */
protected subscribeLineToEvents(anchor: Snap.Element): void;


/**
     * Updated the `regionData` based on the new ghost anchor location. Should be redefined in child classes.
     * @param p - The new ghost anchor location.
     */
protected updateRegion(p: Point2D): void;


/**
     * Callback for the pointerenter event for the ghost anchor.
     * @param e - PointerEvent object.
     */
protected onGhostPointerEnter(e: PointerEvent): void;


/**
     * Callback for the pointermove event for the ghost anchor.
     * @param e - PointerEvent object.
     */
protected onGhostPointerMove(e: PointerEvent): void;


/**
     * Callback for the pointerup event for the ghost anchor.
     * @param e - PointerEvent object.
     */
protected onGhostPointerUp(e: PointerEvent): void;


props => props.mainColor


props => (props.value > 100 ? 100 : props.value)


(value: number): string => {
  return new Intl.NumberFormat('pt-BR').format(value);
}


({
  dateLimit,
  daysLabel,
}: GoalDateRemainingProps) => {
  const now = new Date().getTime();
  const [day, month, year] = dateLimit.split('/');
  const limit = new Date(`${year}-${month}-${day}`).getTime();
  const total = Math.ceil((limit - now) / (1000 * 60 * 60 * 24));

  return <span>{daysLabel(total)}</span>;
}


({
  data,
  mainColor,
  currencyLabel,
  donationsLabel,
  goalLabel,
  goalDateLimit,
  daysLabel,
}) => {
  const stats = JSON.parse(data || '{}');

  if (stats.goal && parseInt(stats.goal) === 1) {
    return (<DonationStatsStyles mainColor={mainColor}>
      <Currency mainColor={mainColor}>
        {`R$ ${currencyInt(stats.pledged)}`}
        {currencyLabel}
      </Currency>
    </DonationStatsStyles>);
  }


(total: number) => (total > 1 ? 'apoios' : 'apoio')


total => {
    if (total === 0) return 'último dia!';
    else if (total > 0 && total < 7) return 'últimos dias!';
    else if (total === 7) return 'última semana';
    else return `faltam ${total} dias`;
  }


type CurrencyProps = {
  mainColor: string;
};


type DonationStatsStylesProps = {
  mainColor: string;
};


type ProgressProps = {
  mainColor: string;
  value: number;
};


type Props = {
  mainColor: string;
  currencyLabel?: string;
  donationsLabel?: (total: number) => string;
  daysLabel?: (total: number) => string;
  goalLabel?: string;
  data?: string;
  goalDateLimit?: string;
};


type GoalDateRemainingProps = {
  dateLimit: string;
  daysLabel: (total: number) => string;
};


daysLabel(total)


/**
 * @class
 */
class Base extends EventEmitter {
keytype: String
fetch: any
key: String
ops: Object
database: any
    public constructor(ops: BaseOptions) {
   super()
this.ops = ops;
this.keytype =  ops?.keytype ? ops.keytype : 'Bearer'
    this.fetch =  fetch;
    this.key = ops?.key
}
error(err:any) {
    if(!(err instanceof Error)) err = new Error(err)
    err.name = 'ShadowWrapper'
    this.emit('error', err)
    }
toString() {
    return __dirname
}

async getResponse(url:String, prams: ResponseParams) {
   // console.log(2)
    if(!(typeof url === 'string')) return this.error('INVALID_URL')
  if(!(typeof prams === 'object')){
      prams = {}
      prams.method = 'GET'
      prams.headers = {
        'Content-Type': 'application/json',
        'User-Agent': `ShadowApi/${require('../package.json').version} (api/nodejs)`,
        'Authorization': `${this.keytype} ${this.key}`
    }

    }

//   if(!(prams.headers['Content-Type'])) prams.headers['Content-Type'] =  'application/json' //this.getHeaders(url)['Content-Type']
//   if(!(prams.headers['Authorization'])) prams.headers['Authorization'] = `${this.type} ${this.key}`
  url = `${this.database ? `https://sus.neongamerbotqk.repl.co/db/v1/` : base}${url}`
 // console.log(url)
let res = await this.fetch(url, prams)
return new Data.Data(res, await res.json())
}
getHeaders(endpoint:String) {
    return {
        method: 'GET',
        headers: {
        'Content-Type': endpoint !==  '/nono' ? 'application/json' : undefined,
       'User-Agent': `ShadowApi/${require('../package.json').version} `,
        'Authorization': `${this.keytype} ${this.key}`
    },
}
}
}


error(err:any) {
    if(!(err instanceof Error)) err = new Error(err)
    err.name = 'ShadowWrapper'
    this.emit('error', err)
    }


toString() {
    return __dirname
}


async getResponse(url:String, prams: ResponseParams) {
   // console.log(2)
    if(!(typeof url === 'string')) return this.error('INVALID_URL')
  if(!(typeof prams === 'object')){
      prams = {}
      prams.method = 'GET'
      prams.headers = {
        'Content-Type': 'application/json',
        'User-Agent': `ShadowApi/${require('../package.json').version} (api/nodejs)`,
        'Authorization': `${this.keytype} ${this.key}`
    }

    }

//   if(!(prams.headers['Content-Type'])) prams.headers['Content-Type'] =  'application/json' //this.getHeaders(url)['Content-Type']
//   if(!(prams.headers['Authorization'])) prams.headers['Authorization'] = `${this.type} ${this.key}`
  url = `${this.database ? `https://sus.neongamerbotqk.repl.co/db/v1/` : base}${url}`
 // console.log(url)
let res = await this.fetch(url, prams)
return new Data.Data(res, await res.json())
}


getHeaders(endpoint:String) {
    return {
        method: 'GET',
        headers: {
        'Content-Type': endpoint !==  '/nono' ? 'application/json' : undefined,
       'User-Agent': `ShadowApi/${require('../package.json').version} `,
        'Authorization': `${this.keytype} ${this.key}`
    },
}
}


(props: PrivateRouteProps) => {

    const { component: Component, ...rest } = props;
      return (
    <Route
      {...rest}
      render={props


// import { getJWT } from '../../localStorage';
interface PrivateRouteProps {
    component: any,
}


export class ChainMaker {
    public makeChain(iterator: IterableIterator<ProcessingLinkInterface> | ProcessingLinkInterface[]): ProcessingLinkInterface {
        let firstLink: ProcessingLinkInterface;
        let prevLink: ProcessingLinkInterface;
        for (let link of iterator) {
            if (!prevLink) {
                firstLink = link;
            } else {
                prevLink.setSuccessor(link);
            }
            prevLink = link;
        }
        return firstLink;
    }
}


public makeChain(iterator: IterableIterator<ProcessingLinkInterface> | ProcessingLinkInterface[]): ProcessingLinkInterface {
        let firstLink: ProcessingLinkInterface;
        let prevLink: ProcessingLinkInterface;
        for (let link of iterator) {
            if (!prevLink) {
                firstLink = link;
            } else {
                prevLink.setSuccessor(link);
            }
            prevLink = link;
        }
        return firstLink;
    }


export default function wrapDisplayName(
  BaseComponent: React.ComponentType<any>,
  hocName: string
) {
  const previousDisplayName = BaseComponent.displayName ?? BaseComponent.name;

  return `${hocName}(${previousDisplayName ?? 'Component'})`;
}


(params) => new Promise(async (res, rej) => {
	// Get 'post_id' parameter
	const [postId, postIdErr] = $(params.post_id).id().$;
	if (postIdErr) return rej('invalid post_id param');

	// Lookup post
	const post = await Post.findOne({
		_id: postId
	});

	if (post === null) {
		return rej('post not found');
	}

	const startTime = new Date(new Date().setMonth(new Date().getMonth() - 1));

	const reactions = await Reaction
		.find({
			post_id: post._id,
			$or: [
				{ deleted_at: { $exists: false } },
				{ deleted_at: { $gt: startTime } }
			]
		}, {
			sort: {
				_id: -1
			},
			fields: {
				_id: false,
				post_id: false
			}
		});

	const graph = [];

	for (let i = 0; i < 30; i++) {
		let day = new Date(new Date().setDate(new Date().getDate() - i));
		day = new Date(day.setMilliseconds(999));
		day = new Date(day.setSeconds(59));
		day = new Date(day.setMinutes(59));
		day = new Date(day.setHours(23));
		// day = day.getTime();

		const count = reactions.filter(r =>
			r.created_at < day && (r.deleted_at == null || r.deleted_at > day)
		).length;

		graph.push({
			date: {
				year: day.getFullYear(),
				month: day.getMonth() + 1, // In JavaScript, month is zero-based.
				day: day.getDate()
			},
			count: count
		});
	}

	res(graph);
})


async (res, rej) => {
	// Get 'post_id' parameter
	const [postId, postIdErr] = $(params.post_id).id().$;
	if (postIdErr) return rej('invalid post_id param');

	// Lookup post
	const post = await Post.findOne({
		_id: postId
	});

	if (post === null) {
		return rej('post not found');
	}

	const startTime = new Date(new Date().setMonth(new Date().getMonth() - 1));

	const reactions = await Reaction
		.find({
			post_id: post._id,
			$or: [
				{ deleted_at: { $exists: false } },
				{ deleted_at: { $gt: startTime } }
			]
		}, {
			sort: {
				_id: -1
			},
			fields: {
				_id: false,
				post_id: false
			}
		});

	const graph = [];

	for (let i = 0; i < 30; i++) {
		let day = new Date(new Date().setDate(new Date().getDate() - i));
		day = new Date(day.setMilliseconds(999));
		day = new Date(day.setSeconds(59));
		day = new Date(day.setMinutes(59));
		day = new Date(day.setHours(23));
		// day = day.getTime();

		const count = reactions.filter(r =>
			r.created_at < day && (r.deleted_at == null || r.deleted_at > day)
		).length;

		graph.push({
			date: {
				year: day.getFullYear(),
				month: day.getMonth() + 1, // In JavaScript, month is zero-based.
				day: day.getDate()
			},
			count: count
		});
	}

	res(graph);
}


r =>
			r.created_at < day && (r.deleted_at == null || r.deleted_at > day)


/**
 * Gets the "code root" as an absolute path.
 *
 * - For "code" configs this is the `projectRoot` field.
 * - For "template" configs this is the `CodeUri` field in the template.
 */
export function getCodeRoot(
    folder: vscode.WorkspaceFolder | undefined,
    config: AwsSamDebuggerConfiguration
): string | undefined {
    switch (config.invokeTarget.target) {
        case 'code': {
            const codeInvoke = config.invokeTarget as CodeTargetProperties
            return pathutil.normalize(tryGetAbsolutePath(folder, codeInvoke.projectRoot))
        }
        case 'api':
        case 'template': {
            const templateInvoke = config.invokeTarget as TemplateTargetProperties
            const template = getTemplate(folder, config)
            if (!template) {
                return undefined
            }
            const templateResource = getTemplateResource(folder, config)
            if (!templateResource?.Properties) {
                return undefined
            }
            const fullPath = tryGetAbsolutePath(folder, templateInvoke.templatePath)
            const templateDir = path.dirname(fullPath)
            // Image lambda or ZIP lambda?
            const isImageLambda = CloudFormation.isImageLambdaResource(templateResource.Properties)
            const uri = isImageLambda
                ? CloudFormation.getStringForProperty(templateResource?.Metadata, 'DockerContext', template)
                : CloudFormation.getStringForProperty(
                      templateResource.Properties as CloudFormation.ZipResourceProperties,
                      'CodeUri',
                      template
                  )
            return uri !== undefined ? pathutil.normalize(path.resolve(templateDir ?? '', uri)) : undefined
        }
        default: {
            throw Error('invalid invokeTarget') // Must not happen.
        }
    }
}


/**
 * Gets the lambda handler name specified in the given config.
 */
export function getHandlerName(
    folder: vscode.WorkspaceFolder | undefined,
    config: AwsSamDebuggerConfiguration
): string {
    switch (config.invokeTarget.target) {
        case 'code': {
            const codeInvoke = config.invokeTarget as CodeTargetProperties
            return codeInvoke.lambdaHandler
        }
        case 'api':
        case 'template': {
            const template = getTemplate(folder, config)
            if (!template) {
                return ''
            }
            const templateResource = getTemplateResource(folder, config)
            if (CloudFormation.isImageLambdaResource(templateResource?.Properties)) {
                return config.invokeTarget.logicalId
            }

            const propertyValue = CloudFormation.resolvePropertyWithOverrides(
                templateResource?.Properties?.Handler,
                template,
                config.sam?.template?.parameters
            )
            return propertyValue ? propertyValue.toString() : ''
        }
        default: {
            // Should never happen.
            vscode.window.showErrorMessage(
                localize(
                    'AWS.sam.debugger.invalidTarget',
                    'Debug Configuration has an unsupported target type. Supported types: {0}',
                    AWS_SAM_DEBUG_TARGET_TYPES.join(', ')
                )
            )
            return ''
        }
    }
}


/** Gets a template object from the given config. */
export function getTemplate(
    folder: vscode.WorkspaceFolder | undefined,
    config: AwsSamDebuggerConfiguration
): CloudFormation.Template | undefined {
    if (!['api', 'template'].includes(config.invokeTarget.target)) {
        return undefined
    }
    const templateInvoke = config.invokeTarget as TemplateTargetProperties
    const fullPath = tryGetAbsolutePath(folder, templateInvoke.templatePath)
    const cfnTemplate = globals.templateRegistry.getRegisteredItem(fullPath)?.item
    return cfnTemplate
}


/**
 * Gets the template resources object specified by the `logicalId`
 * field (if the config has `target=template` or `target=api`).
 */
export function getTemplateResource(
    folder: vscode.WorkspaceFolder | undefined,
    config: AwsSamDebuggerConfiguration
): CloudFormation.Resource | undefined {
    if (!['api', 'template'].includes(config.invokeTarget.target)) {
        return undefined
    }
    const templateInvoke = config.invokeTarget as TemplateTargetProperties
    const cfnTemplate = getTemplate(folder, config)
    if (!cfnTemplate) {
        throw Error(`template not found (not registered?): ${templateInvoke.templatePath}`)
    }
    if (!cfnTemplate?.Resources) {
        throw Error(`no Resources in template: ${templateInvoke.templatePath}`)
    }
    const templateResource: CloudFormation.Resource | undefined = cfnTemplate?.Resources![templateInvoke.logicalId!]
    if (!templateResource) {
        throw Error(
            `template Resources object does not contain key '${templateInvoke.logicalId}':` +
                ` ${JSON.stringify(cfnTemplate?.Resources)}`
        )
    }
    return templateResource
}


/**
 * Checks if the current configuration is based on an Image-based template.
 *
 * Intended for use only by the language-specific `makeConfig` functions.
 */
export function isImageLambdaConfig(config: SamLaunchRequestArgs): boolean {
    const templateResource = getTemplateResource(config.workspaceFolder, config)

    return CloudFormation.isImageLambdaResource(templateResource?.Properties)
}


export function getArchitecture(
    template: CloudFormation.Template | undefined,
    resource: CloudFormation.Resource | undefined,
    invokeTarget: AwsSamDebuggerConfiguration['invokeTarget']
): Architecture | undefined {
    let arch: string | undefined
    let architectureLocation: string
    if (template) {
        arch = (CloudFormation.getArrayForProperty(resource?.Properties, 'Architectures', template) ?? [])[0]
        architectureLocation = localize('AWS.generic.template', 'template file')
    } else {
        arch = (invokeTarget as CodeTargetProperties)?.architecture
        architectureLocation = localize('AWS.generic.launchConfig', 'launch configuration')
    }

    if (arch) {
        const isArch = isArchitecture(arch)

        if (!isArch) {
            getLogger('channel').warn('SAM Invoke: Invalid architecture. Defaulting to x86_64.')
            vscode.window.showWarningMessage(
                localize(
                    'AWS.output.sam.invalidArchitecture',
                    'Invalid architecture specified in {0}. Defaulting to x86_64 architecture for invocation.',
                    architectureLocation
                )
            )
            arch = 'x86_64'
        }

        return arch as Architecture
    }
}


function isArchitecture(a: any): a is Architecture {
    return ['x86_64', 'arm64'].includes(a)
}


export interface NodejsDebugConfiguration extends SamLaunchRequestArgs {
    readonly runtimeFamily: RuntimeFamily.NodeJS
    readonly preLaunchTask?: string
    readonly address: 'localhost'
    readonly localRoot: string
    readonly remoteRoot: string
    readonly skipFiles?: string[]
    readonly port: number
}


export interface PythonPathMapping {
    localRoot: string
    remoteRoot: string
}


export interface PythonDebugConfiguration extends SamLaunchRequestArgs {
    readonly runtimeFamily: RuntimeFamily.Python
    /** Passed to "sam build --manifest …" */
    readonly manifestPath: string | undefined

    // Fields expected by the VSCode debugpy adapter.
    readonly host: string
    readonly port: number
    readonly pathMappings: PythonPathMapping[]
}


/** Alternative (Cloud9) Python debugger: ikp3db */
export interface PythonCloud9DebugConfiguration extends SamLaunchRequestArgs {
    readonly runtimeFamily: RuntimeFamily.Python
    /** Passed to "sam build --manifest …" */
    readonly manifestPath: string | undefined

    // Fields expected by the Cloud9 debug adapter.
    // (Cloud9 sourcefile: debugger-vscode-mainthread-adapter.ts)
    readonly port: number
    readonly address: string
    readonly localRoot: string
    readonly remoteRoot: string
}


export interface DotNetCoreDebugConfiguration extends SamLaunchRequestArgs {
    readonly runtimeFamily: RuntimeFamily.DotNetCore
    processName: string
    pipeTransport: PipeTransport
    windows: {
        pipeTransport: PipeTransport
    }
    sourceFileMap?: {
        [key: string]: string
    }
}


export interface GoDebugConfiguration extends SamLaunchRequestArgs {
    readonly runtimeFamily: RuntimeFamily.Go
    readonly preLaunchTask?: string
    readonly host: 'localhost'
    readonly port: number
}


export interface PipeTransport {
    pipeProgram: 'sh' | 'powershell'
    pipeArgs: string[]
    debuggerPath: typeof DOTNET_CORE_DEBUGGER_PATH
    pipeCwd: string
}


() => {
      this.auth.logout();
      overlayRef.dispose();
    }


() => {
      overlayRef.dispose();
      this.open.focus();
    }


() => {
      overlayRef.dispose();
    }


@Component({
  selector: 'ztp-toolbar-menu-icon',
  templateUrl: './toolbar-menu-icon.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ToolbarMenuIconComponent {
  @ViewChild('open', { static: true }) open: MatButton;

  constructor(
    private el: ElementRef,
    private overlay: OverlayService,
    private auth: AuthFacade
  ) {}

  public showDropDownMenu() {
    this.auth.loadLoggedInUser();

    const { overlayRef, componentRef } = this.overlay.createOverlay<
      DropDownMenuComponent
    >(this.el, DropDownMenuComponent);

    componentRef.instance.logout.subscribe(() => {
      this.auth.logout();
      overlayRef.dispose();
    });

    // dismiss is only called form pushing the escape key
    // set focus back to the open button
    componentRef.instance.dismiss.subscribe(() => {
      overlayRef.dispose();
      this.open.focus();
    });

    overlayRef.backdropClick().subscribe(() => {
      overlayRef.dispose();
    });
  }
}


public showDropDownMenu() {
    this.auth.loadLoggedInUser();

    const { overlayRef, componentRef } = this.overlay.createOverlay<
      DropDownMenuComponent
    >(this.el, DropDownMenuComponent);

    componentRef.instance.logout.subscribe(() => {
      this.auth.logout();
      overlayRef.dispose();
    });

    // dismiss is only called form pushing the escape key
    // set focus back to the open button
    componentRef.instance.dismiss.subscribe(() => {
      overlayRef.dispose();
      this.open.focus();
    });

    overlayRef.backdropClick().subscribe(() => {
      overlayRef.dispose();
    });
  }


() => {
    Record = proxyquire('../src/record', {
      '@google-cloud/promisify': fakePromisify,
    }).Record;
  }


() => {
    record = new Record(ZONE, TYPE, METADATA);
  }