async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";
            const expectedOptions = {
                otherArgs: {
                    headers: {
                        'x-goog-request-params': expectedHeaderRequestParams,
                    },
                },
            };
            const expectedResponse = [
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
            ];
            client.innerApiCalls.listAssets = stubSimpleCall(expectedResponse);
            const [response] = await client.listAssets(request);
            assert.deepStrictEqual(response, expectedResponse);
            assert((client.innerApiCalls.listAssets as SinonStub)
                .getCall(0).calledWith(request, expectedOptions, undefined));
        }


async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";
            const expectedOptions = {
                otherArgs: {
                    headers: {
                        'x-goog-request-params': expectedHeaderRequestParams,
                    },
                },
            };
            const expectedResponse = [
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
            ];
            client.innerApiCalls.listAssets = stubSimpleCallWithCallback(expectedResponse);
            const promise = new Promise((resolve, reject) => {
                 client.listAssets(
                    request,
                    (err?: Error|null, result?: protos.google.cloud.asset.v1p5beta1.IAsset[]|null) => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve(result);
                        }
                    });
            });
            const response = await promise;
            assert.deepStrictEqual(response, expectedResponse);
            assert((client.innerApiCalls.listAssets as SinonStub)
                .getCall(0).calledWith(request, expectedOptions /*, callback defined above */));
        }


(resolve, reject) => {
                 client.listAssets(
                    request,
                    (err?: Error|null, result?: protos.google.cloud.asset.v1p5beta1.IAsset[]|null) => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve(result);
                        }
                    });
            }


(err?: Error|null, result?: protos.google.cloud.asset.v1p5beta1.IAsset[]|null) => {
                        if (err) {
                            reject(err);
                        } else {
                            resolve(result);
                        }
                    }


async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";
            const expectedOptions = {
                otherArgs: {
                    headers: {
                        'x-goog-request-params': expectedHeaderRequestParams,
                    },
                },
            };
            const expectedError = new Error('expected');
            client.innerApiCalls.listAssets = stubSimpleCall(undefined, expectedError);
            await assert.rejects(client.listAssets(request), expectedError);
            assert((client.innerApiCalls.listAssets as SinonStub)
                .getCall(0).calledWith(request, expectedOptions, undefined));
        }


async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";
            const expectedResponse = [
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
            ];
            client.descriptors.page.listAssets.createStream = stubPageStreamingCall(expectedResponse);
            const stream = client.listAssetsStream(request);
            const promise = new Promise((resolve, reject) => {
                const responses: protos.google.cloud.asset.v1p5beta1.Asset[] = [];
                stream.on('data', (response: protos.google.cloud.asset.v1p5beta1.Asset) => {
                    responses.push(response);
                });
                stream.on('end', () => {
                    resolve(responses);
                });
                stream.on('error', (err: Error) => {
                    reject(err);
                });
            });
            const responses = await promise;
            assert.deepStrictEqual(responses, expectedResponse);
            assert((client.descriptors.page.listAssets.createStream as SinonStub)
                .getCall(0).calledWith(client.innerApiCalls.listAssets, request));
            assert.strictEqual(
                (client.descriptors.page.listAssets.createStream as SinonStub)
                    .getCall(0).args[2].otherArgs.headers['x-goog-request-params'],
                expectedHeaderRequestParams
            );
        }


(resolve, reject) => {
                const responses: protos.google.cloud.asset.v1p5beta1.Asset[] = [];
                stream.on('data', (response: protos.google.cloud.asset.v1p5beta1.Asset) => {
                    responses.push(response);
                });
                stream.on('end', () => {
                    resolve(responses);
                });
                stream.on('error', (err: Error) => {
                    reject(err);
                });
            }


(response: protos.google.cloud.asset.v1p5beta1.Asset) => {
                    responses.push(response);
                }


() => {
                    resolve(responses);
                }


(err: Error) => {
                    reject(err);
                }


async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";
            const expectedError = new Error('expected');
            client.descriptors.page.listAssets.createStream = stubPageStreamingCall(undefined, expectedError);
            const stream = client.listAssetsStream(request);
            const promise = new Promise((resolve, reject) => {
                const responses: protos.google.cloud.asset.v1p5beta1.Asset[] = [];
                stream.on('data', (response: protos.google.cloud.asset.v1p5beta1.Asset) => {
                    responses.push(response);
                });
                stream.on('end', () => {
                    resolve(responses);
                });
                stream.on('error', (err: Error) => {
                    reject(err);
                });
            });
            await assert.rejects(promise, expectedError);
            assert((client.descriptors.page.listAssets.createStream as SinonStub)
                .getCall(0).calledWith(client.innerApiCalls.listAssets, request));
            assert.strictEqual(
                (client.descriptors.page.listAssets.createStream as SinonStub)
                    .getCall(0).args[2].otherArgs.headers['x-goog-request-params'],
                expectedHeaderRequestParams
            );
        }


async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
              credentials: {client_email: 'bogus', private_key: 'bogus'},
              projectId: 'bogus',
        });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";
            const expectedResponse = [
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
              generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.Asset()),
            ];
            client.descriptors.page.listAssets.asyncIterate = stubAsyncIterationCall(expectedResponse);
            const responses: protos.google.cloud.asset.v1p5beta1.IAsset[] = [];
            const iterable = client.listAssetsAsync(request);
            for await (const resource of iterable) {
                responses.push(resource!);
            }
            assert.deepStrictEqual(responses, expectedResponse);
            assert.deepStrictEqual(
                (client.descriptors.page.listAssets.asyncIterate as SinonStub)
                    .getCall(0).args[1], request);
            assert.strictEqual(
                (client.descriptors.page.listAssets.asyncIterate as SinonStub)
                    .getCall(0).args[2].otherArgs.headers['x-goog-request-params'],
                expectedHeaderRequestParams
            );
        }


async () => {
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            const request = generateSampleMessage(new protos.google.cloud.asset.v1p5beta1.ListAssetsRequest());
            request.parent = '';
            const expectedHeaderRequestParams = "parent=";const expectedError = new Error('expected');
            client.descriptors.page.listAssets.asyncIterate = stubAsyncIterationCall(undefined, expectedError);
            const iterable = client.listAssetsAsync(request);
            await assert.rejects(async () => {
                const responses: protos.google.cloud.asset.v1p5beta1.IAsset[] = [];
                for await (const resource of iterable) {
                    responses.push(resource!);
                }
            });
            assert.deepStrictEqual(
                (client.descriptors.page.listAssets.asyncIterate as SinonStub)
                    .getCall(0).args[1], request);
            assert.strictEqual(
                (client.descriptors.page.listAssets.asyncIterate as SinonStub)
                    .getCall(0).args[2].otherArgs.headers['x-goog-request-params'],
                expectedHeaderRequestParams
            );
        }


async () => {
                const responses: protos.google.cloud.asset.v1p5beta1.IAsset[] = [];
                for await (const resource of iterable) {
                    responses.push(resource!);
                }
            }


() => {

        describe('accessLevel', () => {
            const fakePath = "/rendered/path/accessLevel";
            const expectedParameters = {
                access_policy: "accessPolicyValue",
                access_level: "accessLevelValue",
            };
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            client.pathTemplates.accessLevelPathTemplate.render =
                sinon.stub().returns(fakePath);
            client.pathTemplates.accessLevelPathTemplate.match =
                sinon.stub().returns(expectedParameters);

            it('accessLevelPath', () => {
                const result = client.accessLevelPath("accessPolicyValue", "accessLevelValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.accessLevelPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            });

            it('matchAccessPolicyFromAccessLevelName', () => {
                const result = client.matchAccessPolicyFromAccessLevelName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.accessLevelPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });

            it('matchAccessLevelFromAccessLevelName', () => {
                const result = client.matchAccessLevelFromAccessLevelName(fakePath);
                assert.strictEqual(result, "accessLevelValue");
                assert((client.pathTemplates.accessLevelPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });
        });

        describe('accessPolicy', () => {
            const fakePath = "/rendered/path/accessPolicy";
            const expectedParameters = {
                access_policy: "accessPolicyValue",
            };
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            client.pathTemplates.accessPolicyPathTemplate.render =
                sinon.stub().returns(fakePath);
            client.pathTemplates.accessPolicyPathTemplate.match =
                sinon.stub().returns(expectedParameters);

            it('accessPolicyPath', () => {
                const result = client.accessPolicyPath("accessPolicyValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.accessPolicyPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            });

            it('matchAccessPolicyFromAccessPolicyName', () => {
                const result = client.matchAccessPolicyFromAccessPolicyName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.accessPolicyPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });
        });

        describe('servicePerimeter', () => {
            const fakePath = "/rendered/path/servicePerimeter";
            const expectedParameters = {
                access_policy: "accessPolicyValue",
                service_perimeter: "servicePerimeterValue",
            };
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            client.pathTemplates.servicePerimeterPathTemplate.render =
                sinon.stub().returns(fakePath);
            client.pathTemplates.servicePerimeterPathTemplate.match =
                sinon.stub().returns(expectedParameters);

            it('servicePerimeterPath', () => {
                const result = client.servicePerimeterPath("accessPolicyValue", "servicePerimeterValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.servicePerimeterPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            });

            it('matchAccessPolicyFromServicePerimeterName', () => {
                const result = client.matchAccessPolicyFromServicePerimeterName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.servicePerimeterPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });

            it('matchServicePerimeterFromServicePerimeterName', () => {
                const result = client.matchServicePerimeterFromServicePerimeterName(fakePath);
                assert.strictEqual(result, "servicePerimeterValue");
                assert((client.pathTemplates.servicePerimeterPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });
        });
    }


() => {
            const fakePath = "/rendered/path/accessLevel";
            const expectedParameters = {
                access_policy: "accessPolicyValue",
                access_level: "accessLevelValue",
            };
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            client.pathTemplates.accessLevelPathTemplate.render =
                sinon.stub().returns(fakePath);
            client.pathTemplates.accessLevelPathTemplate.match =
                sinon.stub().returns(expectedParameters);

            it('accessLevelPath', () => {
                const result = client.accessLevelPath("accessPolicyValue", "accessLevelValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.accessLevelPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            });

            it('matchAccessPolicyFromAccessLevelName', () => {
                const result = client.matchAccessPolicyFromAccessLevelName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.accessLevelPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });

            it('matchAccessLevelFromAccessLevelName', () => {
                const result = client.matchAccessLevelFromAccessLevelName(fakePath);
                assert.strictEqual(result, "accessLevelValue");
                assert((client.pathTemplates.accessLevelPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });
        }


() => {
                const result = client.accessLevelPath("accessPolicyValue", "accessLevelValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.accessLevelPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            }


() => {
                const result = client.matchAccessPolicyFromAccessLevelName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.accessLevelPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            }


() => {
                const result = client.matchAccessLevelFromAccessLevelName(fakePath);
                assert.strictEqual(result, "accessLevelValue");
                assert((client.pathTemplates.accessLevelPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            }


() => {
            const fakePath = "/rendered/path/accessPolicy";
            const expectedParameters = {
                access_policy: "accessPolicyValue",
            };
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            client.pathTemplates.accessPolicyPathTemplate.render =
                sinon.stub().returns(fakePath);
            client.pathTemplates.accessPolicyPathTemplate.match =
                sinon.stub().returns(expectedParameters);

            it('accessPolicyPath', () => {
                const result = client.accessPolicyPath("accessPolicyValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.accessPolicyPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            });

            it('matchAccessPolicyFromAccessPolicyName', () => {
                const result = client.matchAccessPolicyFromAccessPolicyName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.accessPolicyPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });
        }


() => {
                const result = client.accessPolicyPath("accessPolicyValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.accessPolicyPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            }


() => {
                const result = client.matchAccessPolicyFromAccessPolicyName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.accessPolicyPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            }


() => {
            const fakePath = "/rendered/path/servicePerimeter";
            const expectedParameters = {
                access_policy: "accessPolicyValue",
                service_perimeter: "servicePerimeterValue",
            };
            const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
                credentials: {client_email: 'bogus', private_key: 'bogus'},
                projectId: 'bogus',
            });
            client.initialize();
            client.pathTemplates.servicePerimeterPathTemplate.render =
                sinon.stub().returns(fakePath);
            client.pathTemplates.servicePerimeterPathTemplate.match =
                sinon.stub().returns(expectedParameters);

            it('servicePerimeterPath', () => {
                const result = client.servicePerimeterPath("accessPolicyValue", "servicePerimeterValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.servicePerimeterPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            });

            it('matchAccessPolicyFromServicePerimeterName', () => {
                const result = client.matchAccessPolicyFromServicePerimeterName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.servicePerimeterPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });

            it('matchServicePerimeterFromServicePerimeterName', () => {
                const result = client.matchServicePerimeterFromServicePerimeterName(fakePath);
                assert.strictEqual(result, "servicePerimeterValue");
                assert((client.pathTemplates.servicePerimeterPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            });
        }


() => {
                const result = client.servicePerimeterPath("accessPolicyValue", "servicePerimeterValue");
                assert.strictEqual(result, fakePath);
                assert((client.pathTemplates.servicePerimeterPathTemplate.render as SinonStub)
                    .getCall(-1).calledWith(expectedParameters));
            }


() => {
                const result = client.matchAccessPolicyFromServicePerimeterName(fakePath);
                assert.strictEqual(result, "accessPolicyValue");
                assert((client.pathTemplates.servicePerimeterPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            }


() => {
                const result = client.matchServicePerimeterFromServicePerimeterName(fakePath);
                assert.strictEqual(result, "servicePerimeterValue");
                assert((client.pathTemplates.servicePerimeterPathTemplate.match as SinonStub)
                    .getCall(-1).calledWith(fakePath));
            }


[Symbol.asyncIterator]() {
            return {
                async next() {
                    if (error) {
                        return Promise.reject(error);
                    }
                    if (counter >= responses!.length) {
                        return Promise.resolve({done: true, value: undefined});
                    }
                    return Promise.resolve({done: false, value: responses![counter++]});
                }
            };
        }


async next() {
                    if (error) {
                        return Promise.reject(error);
                    }
                    if (counter >= responses!.length) {
                        return Promise.resolve({done: true, value: undefined});
                    }
                    return Promise.resolve({done: false, value: responses![counter++]});
                }


(nth: number) => {
    const s = nth % 100;
    if (s > 3 && s < 21) return "th";
    switch (s % 10) {
      case 1:
        return "st";
      case 2:
        return "nd";
      case 3:
        return "rd";
      default:
        return "th";
    }
  }


function AllServices() {
  return (
    <div>
      <Layout>
        <Headers
          pageName={[{ name: 'Services', route: '/services' }]}
          tabHeaders={['All services', 'Service Groups (clusters)', 'Settings']}
          // 'TODO revamp service global setting'
          Components={[<ServicesList />, <ServiceGroups />, <GlobalServieSetting />]}
        />
      </Layout>
    </div>
  );
}

const ServiceView = () => {
  return (
    <Switch>
      <Route exact path="/services" component={AllServices} />
      <Route path="/services/groups/group/:ID" component={GroupPage} />
      <Route path="/services/service/:ID" component={ServicePage} />
    </Switch>
  );
}


() => {
  return (
    <Switch>
      <Route exact path="/services" component={AllServices} />
      <Route path="/services/groups/group/:ID" component={GroupPage} />
      <Route path="/services/service/:ID" component={ServicePage} />
    </Switch>


(
        event: React.MouseEvent<HTMLButtonElement>,
    ) => {
        if (this.props.isActionDisabled) {
            return;
        }
        this.props.disableAction();

        event.stopPropagation();
        const action = (event.currentTarget.name as any) as ChangeOperation;

        this.props.updateTranslationStatus(this.props.translation.pk, action);
    }


(
        event: React.MouseEvent<HTMLButtonElement>,
    ) => {
        if (this.props.isActionDisabled) {
            return;
        }
        this.props.disableAction();

        event.stopPropagation();
        this.props.deleteTranslation(this.props.translation.pk);
    }


() => {
        if (this.props.isReadOnlyEditor) {
            return;
        }

        // Ignore if selecting text
        if (window.getSelection().toString()) {
            return;
        }

        this.props.updateEditorTranslation(
            this.props.translation.string,
            'history',
        );
    }


(event: React.MouseEvent) => void


(
        event: React.MouseEvent,
    ) => {
        event.stopPropagation();
        this.setState((state) => {
            return { areCommentsVisible: !state.areCommentsVisible };
        });
    }


(state) => {
            return { areCommentsVisible: !state.areCommentsVisible };
        }


(
        event: React.MouseEvent,
    ) => {
        event.stopPropagation();
        this.setState((state) => {
            return { isDiffVisible: !state.isDiffVisible };
        });
    }


(state) => {
            return { isDiffVisible: !state.isDiffVisible };
        }


/**
 * Render a translation in the History tab.
 *
 * Shows the translation's status, date, author and reviewer, as well as
 * the content of the translation.
 *
 * The status can be interact with if the user has sufficient permissions to
 * change said status.
 */
export class TranslationBase extends React.Component<InternalProps, State> {
    constructor(props: InternalProps) {
        super(props);

        this.state = {
            isDiffVisible: false,
            areCommentsVisible: false,
        };
    }

    handleStatusChange: (event: React.MouseEvent<HTMLButtonElement>) => void = (
        event: React.MouseEvent<HTMLButtonElement>,
    ) => {
        if (this.props.isActionDisabled) {
            return;
        }
        this.props.disableAction();

        event.stopPropagation();
        const action = (event.currentTarget.name as any) as ChangeOperation;

        this.props.updateTranslationStatus(this.props.translation.pk, action);
    };

    delete: (event: React.MouseEvent<HTMLButtonElement>) => void = (
        event: React.MouseEvent<HTMLButtonElement>,
    ) => {
        if (this.props.isActionDisabled) {
            return;
        }
        this.props.disableAction();

        event.stopPropagation();
        this.props.deleteTranslation(this.props.translation.pk);
    };

    copyTranslationIntoEditor: () => void = () => {
        if (this.props.isReadOnlyEditor) {
            return;
        }

        // Ignore if selecting text
        if (window.getSelection().toString()) {
            return;
        }

        this.props.updateEditorTranslation(
            this.props.translation.string,
            'history',
        );
    };

    getStatus(): string {
        const { translation } = this.props;

        if (translation.approved) {
            return 'approved';
        }
        if (translation.fuzzy) {
            return 'fuzzy';
        }
        if (translation.rejected) {
            return 'rejected';
        }
        return 'unreviewed';
    }

    getApprovalTitle(): string {
        const { translation } = this.props;

        // TODO: To Localize.
        if (translation.approved && translation.approvedUser) {
            return `Approved by ${translation.approvedUser}`;
        }
        if (translation.unapprovedUser) {
            return `Unapproved by ${translation.unapprovedUser}`;
        }
        return 'Not reviewed yet';
    }

    renderUser(): React.ReactElement<'a'> | React.ReactElement<'span'> {
        const { translation } = this.props;

        if (!translation.uid) {
            return <span>{translation.user}</span>;
        }

        return (
            <a
                href={`/contributors/${translation.username}`}
                title={this.getApprovalTitle()}
                target='_blank'
                rel='noopener noreferrer'
                onClick={(e: React.MouseEvent) => e.stopPropagation()}


type Props = {
    entity: Entity;
    isReadOnlyEditor: boolean;
    isTranslator: boolean;
    translation: HistoryTranslation;
    activeTranslation: HistoryTranslation;
    locale: Locale;
    user: UserState;
    index: number;
    deleteTranslation: (id: number) => void;
    addComment: (comment: string, id: number | null | undefined) => void;
    updateEditorTranslation: (arg0: string, arg1: string) => void;
    updateTranslationStatus: (id: number, operation: ChangeOperation) => void;
};


type InternalProps = Props & {
    isActionDisabled: boolean;
    disableAction: () => void;
};


type State = {
    isDiffVisible: boolean;
    areCommentsVisible: boolean;
};


getStatus(): string {
        const { translation } = this.props;

        if (translation.approved) {
            return 'approved';
        }
        if (translation.fuzzy) {
            return 'fuzzy';
        }
        if (translation.rejected) {
            return 'rejected';
        }
        return 'unreviewed';
    }


getApprovalTitle(): string {
        const { translation } = this.props;

        // TODO: To Localize.
        if (translation.approved && translation.approvedUser) {
            return `Approved by ${translation.approvedUser}`;
        }
        if (translation.unapprovedUser) {
            return `Unapproved by ${translation.unapprovedUser}`;
        }
        return 'Not reviewed yet';
    }


renderUser(): React.ReactElement<'a'> | React.ReactElement<'span'> {
        const { translation } = this.props;

        if (!translation.uid) {
            return <span>{translation.user}</span>;
        }

        return (
            <a
                href={`/contributors/${translation.username}`}


stopPropagation()


if (isReadOnlyEditor) {
            // Copying into the editor is not allowed
            className += ' cannot-copy';
        }


if (isTranslator


registration => !isValueProvider(registration.provider)


registration => {
            registration.instance = undefined;
            return registration;
          }


({options}) => options.lifecycle === Lifecycle.ContainerScoped


registration => {
            if (registration.options.lifecycle === Lifecycle.ContainerScoped) {
              return {
                provider: registration.provider,
                options: registration.options
              };
            }

            return registration;
          }


async (param: ParamInfo, idx: number) => {
      try {
        if (isTokenDescriptor(param)) {
          return param.multiple
            ? await this.resolveAll(param.token)
            : await this.resolve(param.token, context);
        }
        return await this.resolve(param, context);
      } catch (e) {
        throw new Error(formatErrorCtor(ctor, idx, e));
      }
    }


export type Registration<T = any> = {
  provider: Provider<T>;
  options: RegistrationOptions;
  instance?: T;
};


export type ParamInfo = TokenDescriptor | InjectionToken<any>;


/**
   * Register a dependency provider.
   *
   * @param provider {Provider} The dependency provider
   */
public register<T>(
    token: InjectionToken<T>,
    provider: ValueProvider<T>
  ): InternalDependencyContainer;


public register<T>(
    token: InjectionToken<T>,
    provider: FactoryProvider<T>
  ): InternalDependencyContainer;


public register<T>(
    token: InjectionToken<T>,
    provider: TokenProvider<T>,
    options?: RegistrationOptions
  ): InternalDependencyContainer;


public register<T>(
    token: InjectionToken<T>,
    provider: ClassProvider<T>,
    options?: RegistrationOptions
  ): InternalDependencyContainer;


public register<T>(
    token: InjectionToken<T>,
    provider: constructor<T>,
    options?: RegistrationOptions
  ): InternalDependencyContainer;


public register<T>(
    token: InjectionToken<T>,
    providerOrConstructor: Provider<T> | constructor<T>,
    options: RegistrationOptions = {lifecycle: Lifecycle.Transient}
  ): InternalDependencyContainer {
    let provider: Provider<T>;

    if (!isProvider(providerOrConstructor)) {
      provider = {useClass: providerOrConstructor};
    } else {
      provider = providerOrConstructor;
    }

    if (
      options.lifecycle === Lifecycle.Singleton ||
      options.lifecycle == Lifecycle.ContainerScoped ||
      options.lifecycle == Lifecycle.ResolutionScoped
    ) {
      if (isValueProvider(provider) || isFactoryProvider(provider)) {
        throw new Error(
          `Cannot use lifecycle "${
            Lifecycle[options.lifecycle]
          }" with ValueProviders or FactoryProviders`
        );
      }
    }

    this._registry.set(token, {provider, options});

    return this;
  }


public registerType<T>(
    from: InjectionToken<T>,
    to: InjectionToken<T>
  ): InternalDependencyContainer {
    if (isNormalToken(to)) {
      return this.register(from, {
        useToken: to
      });
    }

    return this.register(from, {
      useClass: to
    });
  }


public registerInstance<T>(
    token: InjectionToken<T>,
    instance: T
  ): InternalDependencyContainer {
    return this.register(token, {
      useValue: instance
    });
  }


public registerSingleton<T>(
    from: InjectionToken<T>,
    to: InjectionToken<T>
  ): InternalDependencyContainer;


public registerSingleton<T>(
    token: constructor<T>,
    to?: constructor<any>
  ): InternalDependencyContainer;


public registerSingleton<T>(
    from: InjectionToken<T>,
    to?: InjectionToken<T>
  ): InternalDependencyContainer {
    if (isNormalToken(from)) {
      if (isNormalToken(to)) {
        return this.register(
          from,
          {
            useToken: to
          },
          {lifecycle: Lifecycle.Singleton}
        );
      } else if (to) {
        return this.register(
          from,
          {
            useClass: to
          },
          {lifecycle: Lifecycle.Singleton}
        );
      }

      throw new Error(
        'Cannot register a type name as a singleton without a "to" token'
      );
    }

    let useClass = from;
    if (to && !isNormalToken(to)) {
      useClass = to;
    }

    return this.register(
      from,
      {
        useClass
      },
      {lifecycle: Lifecycle.Singleton}
    );
  }


public async resolve<T>(
    token: InjectionToken<T>,
    context: ResolutionContext = new ResolutionContext()
  ): Promise<T> {
    const registration = this.getRegistration(token);

    if (!registration && isNormalToken(token)) {
      throw new Error(
        `Attempted to resolve unregistered dependency token: "${token.toString()}"`
      );
    }

    if (registration) {
      return await this.resolveRegistration(registration, context);
    }

    // No registration for this token, but since it's a constructor, return an instance
    const resolved = await this.construct(token as constructor<T>, context);
    await callInitializers(this, resolved);
    return resolved;
  }


private resolveRegistration<T>(
    registration: Registration,
    context: ResolutionContext
  ): Promise<T> {
    // If we have already resolved or are already resolving this scoped dependency, return its promise
    if (
      registration.options.lifecycle === Lifecycle.ResolutionScoped &&
      context.scopedResolutions.has(registration)
    ) {
      return context.scopedResolutions.get(registration);
    }
    const resolutionPromise = this.resolveRegistrationHelper<T>(
      registration,
      context
    );
    // If this is a scoped dependency, store promise for the resolved instance in context
    if (registration.options.lifecycle === Lifecycle.ResolutionScoped) {
      context.scopedResolutions.set(registration, resolutionPromise);
    }
    return resolutionPromise;
  }


private async resolveRegistrationHelper<T>(
    registration: Registration,
    context: ResolutionContext
  ): Promise<T> {
    const isSingleton = registration.options.lifecycle === Lifecycle.Singleton;
    const isContainerScoped =
      registration.options.lifecycle === Lifecycle.ContainerScoped;

    const returnInstance = isSingleton || isContainerScoped;

    let resolved: T;

    if (isValueProvider(registration.provider)) {
      resolved = registration.provider.useValue;
    } else if (isTokenProvider(registration.provider)) {
      resolved = returnInstance
        ? registration.instance ||
          (registration.instance = await this.resolve(
            registration.provider.useToken,
            context
          ))
        : await this.resolve(registration.provider.useToken, context);
    } else if (isClassProvider(registration.provider)) {
      resolved = returnInstance
        ? registration.instance ||
          (registration.instance = await this.construct(
            registration.provider.useClass,
            context
          ))
        : await this.construct(registration.provider.useClass, context);
    } else if (isFactoryProvider(registration.provider)) {
      resolved = await registration.provider.useFactory(this);
    } else {
      resolved = await this.construct(registration.provider, context);
    }

    await callInitializers(this, resolved);
    return resolved;
  }


public async resolveAll<T>(
    token: InjectionToken<T>,
    context: ResolutionContext = new ResolutionContext()
  ): Promise<T[]> {
    let registrations = this.getAllRegistrations(token);

    if (!registrations && isNormalToken(token)) {
      registrations = [];
    }

    if (registrations) {
      const instances = [];
      for (const item of registrations) {
        instances.push(await this.resolveRegistration<T>(item, context));
      }
      return instances;
    }

    // No registration for this token, but since it's a constructor, return an instance
    const resolved = await this.construct(token as constructor<T>, context);
    await callInitializers(this, resolved);
    return [resolved];
  }


public isRegistered<T>(token: InjectionToken<T>, recursive = false): boolean {
    return (
      this._registry.has(token) ||
      (recursive &&
        (this.parent || false) &&
        this.parent.isRegistered(token, true))
    );
  }


public reset(): void {
    this._registry.clear();
  }


public clearInstances(): void {
    for (const [token, registrations] of this._registry.entries()) {
      this._registry.setAll(
        token,
        registrations
          // Clear ValueProvider registrations
          .filter(registration => !isValueProvider(registration.provider))
          // Clear instances
          .map(registration => {
            registration.instance = undefined;
            return registration;
          })
      );
    }
  }


public createChildContainer(): DependencyContainer {
    const childContainer = new InternalDependencyContainer(this);

    for (const [token, registrations] of this._registry.entries()) {
      // If there are any ContainerScoped registrations, we need to copy
      // ALL registrations to the child container, if we were to copy just
      // the ContainerScoped registrations, we would lose access to the others
      if (
        registrations.some(
          ({options}) => options.lifecycle === Lifecycle.ContainerScoped
        )
      ) {
        childContainer._registry.setAll(
          token,
          registrations.map<Registration>(registration => {
            if (registration.options.lifecycle === Lifecycle.ContainerScoped) {
              return {
                provider: registration.provider,
                options: registration.options
              };
            }

            return registration;
          })
        );
      }
    }

    return childContainer;
  }


private getRegistration<T>(token: InjectionToken<T>): Registration | null {
    if (this.isRegistered(token)) {
      return this._registry.get(token)!;
    }

    if (this.parent) {
      return this.parent.getRegistration(token);
    }

    return null;
  }


private getAllRegistrations<T>(
    token: InjectionToken<T>
  ): Registration[] | null {
    if (this.isRegistered(token)) {
      return this._registry.getAll(token);
    }

    if (this.parent) {
      return this.parent.getAllRegistrations(token);
    }

    return null;
  }


private async construct<T>(
    ctor: constructor<T>,
    context: ResolutionContext
  ): Promise<T> {
    if (typeof ctor === "undefined") {
      throw new Error(
        "Attempted to construct an undefined constructor. Could mean a circular dependency problem."
      );
    }

    if (ctor.length === 0) {
      return new ctor();
    }

    const paramInfo = typeInfo.get(ctor);

    if (!paramInfo || paramInfo.length === 0) {
      throw new Error(`TypeInfo not known for "${ctor.name}"`);
    }

    let idx = 0;
    const params = [];
    for (const param of paramInfo) {
      const resolver = this.resolveParams(context, ctor);
      params.push(await resolver(param, idx));
      idx++;
    }

    return new ctor(...params);
  }


private resolveParams<T>(context: ResolutionContext, ctor: constructor<T>) {
    return async (param: ParamInfo, idx: number) => {
      try {
        if (isTokenDescriptor(param)) {
          return param.multiple
            ? await this.resolveAll(param.token)
            : await this.resolve(param.token, context);
        }
        return await this.resolve(param, context);
      } catch (e) {
        throw new Error(formatErrorCtor(ctor, idx, e));
      }
    };
  }


() => {
  const targetRef = useRef<HTMLDivElement>(null)

  const onHeightChange = (height: number) => {
    const ratio = height / maxHeight
    console.log(ratio)
    const target = targetRef.current
    if (!target) return
    target.style.height = '100%'
    target.style.backgroundImage = `linear-gradient(rgba(185,147,214,${ratio}),rgba(140,166,219,${ratio}))`
  }

  useEffect(() => {
    onHeightChange(minHeight)
  }, [])

  return (
    <div
      style={{
        padding: 12,
      }}


(height: number) => {
    const ratio = height / maxHeight
    console.log(ratio)
    const target = targetRef.current
    if (!target) return
    target.style.height = '100%'
    target.style.backgroundImage = `linear-gradient(rgba(185,147,214,${ratio}),rgba(140,166,219,${ratio}))`
  }


() => {
    onHeightChange(minHeight)
  }


() => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getTitleText()).toEqual('OCDE app is running!');
  });

  afterEach(async () => {
    // Assert that there are no errors emitted from the browser
    const logs = await browser.manage().logs().get(logging.Type.BROWSER);
    expect(logs).not.toContain(jasmine.objectContaining({
      level: logging.Level.SEVERE,
    } as logging.Entry));
  });
}


() => {
    page.navigateTo();
    expect(page.getTitleText()).toEqual('OCDE app is running!');
  }


({ hideMobile }) => hideMobile && css`
      display: none
    `


({ userProvider, handleConnect }: { userProvider: IProviderUserOptions, handleConnect: (provider: any) => void }) =>
  <Provider
    key


(userProviders: IProviderUserOptions[]) => {
  const providersByName: { [name: string]: IProviderUserOptions } = {}
  for (const userProvider of userProviders) {
    providersByName[userProvider.name] = userProvider
  }
  return providersByName
}


({ userProviders, connectToWallet, changeLanguage, changeTheme, availableLanguages, selectedLanguageCode, selectedTheme }: IWalletProvidersProps) => {
  // the providers that are hardcoded into the layout below
  const hardCodedProviderNames = [
    providers.METAMASK.name, providers.NIFTY.name, providers.LIQUALITY.name, // browser
    providers.WALLETCONNECT.name, // mobile
    providers.PORTIS.name, providers.TORUS.name, // custodial
    LEDGER.name, TREZOR.name, DCENT.name // hardware
  ]

  const providersByName = userProvidersByName(userProviders)

  // additional providers that the developer wants to use
  const developerProviders = Object.keys(providersByName).filter((providerName: string) =>
    !hardCodedProviderNames.includes(providerName) ? providerName : null)

  // handle connect
  const handleConnect = (provider: IProviderUserOptions) => connectToWallet(provider)

  return <>
    <Header2>
      {Object.keys(userProviders).length !== 0 ? <Trans>Connect your wallet</Trans> : <Trans>No wallets found</Trans>}
    </Header2>
    <ProvidersWrapper className={PROVIDERS_WRAPPER_CLASSNAME}>
      <ProviderRow>
        <UserProvider userProvider={providersByName[providers.METAMASK.name] || providers.METAMASK} handleConnect={handleConnect} />
        <UserProvider userProvider={providersByName[providers.NIFTY.name] || providers.NIFTY} handleConnect={handleConnect} />
        <UserProvider userProvider={providersByName[providers.LIQUALITY.name] || providers.LIQUALITY} handleConnect={handleConnect} />
      </ProviderRow>
      <ProviderRow hideMobile={true}>
        <UserProvider userProvider={providersByName[providers.WALLETCONNECT.name] || providers.WALLETCONNECT} handleConnect={handleConnect} />
      </ProviderRow>
      <ProviderRow>
        <UserProvider userProvider={providersByName[providers.PORTIS.name] || providers.PORTIS} handleConnect={handleConnect} />
        <UserProvider userProvider={providersByName[providers.TORUS.name] || providers.TORUS} handleConnect={handleConnect} />
      </ProviderRow>
      <ProviderRow hideMobile={true}>
        <UserProvider userProvider={providersByName[LEDGER.name] || LEDGER} handleConnect={handleConnect} />
        <UserProvider userProvider={providersByName[TREZOR.name] || TREZOR} handleConnect={handleConnect} />
        <UserProvider userProvider={providersByName[DCENT.name] || DCENT} handleConnect={handleConnect} />
      </ProviderRow>
      {developerProviders.length !== 0 && (
        <ProviderRow className={PROVIDERS_DEVELOPER_CLASSNAME}>
          {developerProviders.map((providerName: string) =>
            <UserProvider
              key={providerName}
              userProvider={providersByName[providerName]}
              handleConnect={handleConnect} />
          )}
        </ProviderRow>


(providerName: string) =>
    !hardCodedProviderNames.includes(providerName) ? providerName : null


(provider: IProviderUserOptions) => connectToWallet(provider)


(providerName: string) =>
            <UserProvider
              key


interface IWalletProvidersProps {
  userProviders: IProviderUserOptions[]
  connectToWallet: (provider: IProviderUserOptions) => void
  changeLanguage: (event: any) => void
  changeTheme: (theme: themesOptions) => void
  availableLanguages: { code:string, name:string } []
  selectedLanguageCode: string
  selectedTheme: themesOptions
}


wallet<


@NgModule({
  bootstrap: [LanguageDemoComponent],
  declarations: [LanguageDemoComponent],
  imports: [
    BrowserModule,
    RecaptchaModule.forRoot(),
    DemoWrapperModule,
  ],
  providers: [
    {
      provide: RECAPTCHA_LANGUAGE,
      useValue: 'fr',
    },
    { provide: PAGE_SETTINGS, useValue: settings },
  ],
})
export class DemoModule { }


function subnetTypeTagValue(type: SubnetType) {
  switch (type) {
    case SubnetType.Public: return 'Public';
    case SubnetType.Private: return 'Private';
    case SubnetType.Isolated: return 'Isolated';
  }
}


function ifUndefined<T>(value: T | undefined, defaultValue: T): T {
  return value !== undefined ? value : defaultValue;
}


subnet => (subnet.subnetType !== SubnetType.Isolated)


publicSubnet => {
        publicSubnet.addDefaultIGWRouteEntry(igw, att);
      }


(privateSubnet, i) => {
        let ngwId = this.natGatewayByAZ[privateSubnet.availabilityZone];
        if (ngwId === undefined) {
          const ngwArray = Array.from(Object.values(this.natGatewayByAZ));
          // round robin the available NatGW since one is not in your AZ
          ngwId = ngwArray[i % ngwArray.length];
        }
        privateSubnet.addDefaultNatRouteEntry(ngwId);
      }


subnet => (subnet.subnetType === SubnetType.Private)


(zone, index) => {
      const name = subnetId(subnetConfig.name, index);
      const subnetProps: VpcSubnetProps = {
        availabilityZone: zone,
        vpcId: this.vpcId,
        cidrBlock: this.networkBuilder.addSubnet(cidrMask),
        mapPublicIpOnLaunch: (subnetConfig.subnetType === SubnetType.Public),
      };

      let subnet: VpcSubnet;
      switch (subnetConfig.subnetType) {
        case SubnetType.Public:
          const publicSubnet = new VpcPublicSubnet(this, name, subnetProps);
          this.publicSubnets.push(publicSubnet);
          subnet = publicSubnet;
          break;
        case SubnetType.Private:
          const privateSubnet = new VpcPrivateSubnet(this, name, subnetProps);
          this.privateSubnets.push(privateSubnet);
          subnet = privateSubnet;
          break;
        case SubnetType.Isolated:
          const isolatedSubnet = new VpcPrivateSubnet(this, name, subnetProps);
          this.isolatedSubnets.push(isolatedSubnet);
          subnet = isolatedSubnet;
          break;
        default:
          throw new Error(`Unrecognized subnet type: ${subnetConfig.subnetType}`);
      }

      // These values will be used to recover the config upon provider import
      const includeResourceTypes = [CfnSubnet.resourceTypeName];
      subnet.node.apply(new cdk.Tag(SUBNETNAME_TAG, subnetConfig.name, {includeResourceTypes}));
      subnet.node.apply(new cdk.Tag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), {includeResourceTypes}));
    }


/**
 * Represents a new VPC subnet resource
 */
export class VpcSubnet extends cdk.Construct implements IVpcSubnet {
  public static import(scope: cdk.Construct, id: string, props: VpcSubnetImportProps): IVpcSubnet {
    return new ImportedVpcSubnet(scope, id, props);
  }

  /**
   * The Availability Zone the subnet is located in
   */
  public readonly availabilityZone: string;

  /**
   * The subnetId for this particular subnet
   */
  public readonly subnetId: string;

  /**
   * Parts of this VPC subnet
   */
  public readonly dependencyElements: cdk.IDependable[] = [];

  /**
   * The routeTableId attached to this subnet.
   */
  private readonly routeTableId: string;

  private readonly internetDependencies = new ConcreteDependable();

  constructor(scope: cdk.Construct, id: string, props: VpcSubnetProps) {
    super(scope, id);
    this.node.apply(new cdk.Tag(NAME_TAG, this.node.path));

    this.availabilityZone = props.availabilityZone;
    const subnet = new CfnSubnet(this, 'Subnet', {
      vpcId: props.vpcId,
      cidrBlock: props.cidrBlock,
      availabilityZone: props.availabilityZone,
      mapPublicIpOnLaunch: props.mapPublicIpOnLaunch,
    });
    this.subnetId = subnet.subnetId;
    const table = new CfnRouteTable(this, 'RouteTable', {
      vpcId: props.vpcId,
    });
    this.routeTableId = table.ref;

    // Associate the public route table for this subnet, to this subnet
    new CfnSubnetRouteTableAssociation(this, 'RouteTableAssociation', {
      subnetId: this.subnetId,
      routeTableId: table.ref
    });
  }

  public export(): VpcSubnetImportProps {
    return {
      availabilityZone: new cdk.Output(this, 'AvailabilityZone', { value: this.availabilityZone }).makeImportValue().toString(),
      subnetId: new cdk.Output(this, 'VpcSubnetId', { value: this.subnetId }).makeImportValue().toString(),
    };
  }

  public get internetConnectivityEstablished(): IDependable {
    return this.internetDependencies;
  }

  protected addDefaultRouteToNAT(natGatewayId: string) {
    const route = new CfnRoute(this, `DefaultRoute`, {
      routeTableId: this.routeTableId,
      destinationCidrBlock: '0.0.0.0/0',
      natGatewayId
    });
    this.internetDependencies.add(route);
  }

  /**
   * Create a default route that points to a passed IGW, with a dependency
   * on the IGW's attachment to the VPC.
   */
  protected addDefaultRouteToIGW(
    gateway: CfnInternetGateway,
    gatewayAttachment: CfnVPCGatewayAttachment) {
    const route = new CfnRoute(this, `DefaultRoute`, {
      routeTableId: this.routeTableId,
      destinationCidrBlock: '0.0.0.0/0',
      gatewayId: gateway.ref
    });
    route.node.addDependency(gatewayAttachment);

    // Since the 'route' depends on the gateway attachment, just
    // depending on the route is enough.
    this.internetDependencies.add(route);
  }
}


/**
 * Represents a public VPC subnet resource
 */
export class VpcPublicSubnet extends VpcSubnet {

  constructor(scope: cdk.Construct, id: string, props: VpcSubnetProps) {
    super(scope, id, props);
  }

  /**
   * Create a default route that points to a passed IGW, with a dependency
   * on the IGW's attachment to the VPC.
   */
  public addDefaultIGWRouteEntry(
    gateway: CfnInternetGateway,
    gatewayAttachment: CfnVPCGatewayAttachment) {
    this.addDefaultRouteToIGW(gateway, gatewayAttachment);
  }

  /**
   * Creates a new managed NAT gateway attached to this public subnet.
   * Also adds the EIP for the managed NAT.
   * @returns A ref to the the NAT Gateway ID
   */
  public addNatGateway() {
    // Create a NAT Gateway in this public subnet
    const ngw = new CfnNatGateway(this, `NATGateway`, {
      subnetId: this.subnetId,
      allocationId: new CfnEIP(this, `EIP`, {
        domain: 'vpc'
      }).eipAllocationId,
    });
    return ngw;
  }
}


/**
 * Represents a private VPC subnet resource
 */
export class VpcPrivateSubnet extends VpcSubnet {
  constructor(scope: cdk.Construct, id: string, props: VpcSubnetProps) {
    super(scope, id, props);
  }

  /**
   * Adds an entry to this subnets route table that points to the passed NATGatwayId
   */
  public addDefaultNatRouteEntry(natGatewayId: string) {
    this.addDefaultRouteToNAT(natGatewayId);
  }
}


class ImportedVpcNetwork extends VpcNetworkBase {
  public readonly vpcId: string;
  public readonly publicSubnets: IVpcSubnet[];
  public readonly privateSubnets: IVpcSubnet[];
  public readonly isolatedSubnets: IVpcSubnet[];
  public readonly availabilityZones: string[];

  constructor(scope: cdk.Construct, id: string, private readonly props: VpcNetworkImportProps) {
    super(scope, id);

    this.vpcId = props.vpcId;
    this.availabilityZones = props.availabilityZones;

    // tslint:disable:max-line-length
    const pub = new ImportSubnetGroup(props.publicSubnetIds, props.publicSubnetNames, SubnetType.Public, this.availabilityZones, 'publicSubnetIds', 'publicSubnetNames');
    const priv = new ImportSubnetGroup(props.privateSubnetIds, props.privateSubnetNames, SubnetType.Private, this.availabilityZones, 'privateSubnetIds', 'privateSubnetNames');
    const iso = new ImportSubnetGroup(props.isolatedSubnetIds, props.isolatedSubnetNames, SubnetType.Isolated, this.availabilityZones, 'isolatedSubnetIds', 'isolatedSubnetNames');
    // tslint:enable:max-line-length

    this.publicSubnets = pub.import(this);
    this.privateSubnets = priv.import(this);
    this.isolatedSubnets = iso.import(this);
  }

  public export() {
    return this.props;
  }
}


class ImportedVpcSubnet extends cdk.Construct implements IVpcSubnet {
  public readonly internetConnectivityEstablished: cdk.IDependable = new cdk.ConcreteDependable();
  public readonly availabilityZone: string;
  public readonly subnetId: string;

  constructor(scope: cdk.Construct, id: string, private readonly props: VpcSubnetImportProps) {
    super(scope, id);

    this.subnetId = props.subnetId;
    this.availabilityZone = props.availabilityZone;
  }

  public export() {
    return this.props;
  }
}


/**
 * VpcNetworkProps allows you to specify configuration options for a VPC
 */
export interface VpcNetworkProps {

  /**
   * The CIDR range to use for the VPC (e.g. '10.0.0.0/16'). Should be a minimum of /28 and maximum size of /16.
   * The range will be split evenly into two subnets per Availability Zone (one public, one private).
   */
  cidr?: string;

  /**
   * Indicates whether the instances launched in the VPC get public DNS hostnames.
   * If this attribute is true, instances in the VPC get public DNS hostnames,
   * but only if the enableDnsSupport attribute is also set to true.
   */
  enableDnsHostnames?: boolean;

  /**
   * Indicates whether the DNS resolution is supported for the VPC. If this attribute
   * is false, the Amazon-provided DNS server in the VPC that resolves public DNS hostnames
   * to IP addresses is not enabled. If this attribute is true, queries to the Amazon
   * provided DNS server at the 169.254.169.253 IP address, or the reserved IP address
   * at the base of the VPC IPv4 network range plus two will succeed.
   */
  enableDnsSupport?: boolean;

  /**
   * The default tenancy of instances launched into the VPC.
   * By default, instances will be launched with default (shared) tenancy.
   * By setting this to dedicated tenancy, instances will be launched on hardware dedicated
   * to a single AWS customer, unless specifically specified at instance launch time.
   * Please note, not all instance types are usable with Dedicated tenancy.
   */
  defaultInstanceTenancy?: DefaultInstanceTenancy;

  /**
   * Define the maximum number of AZs to use in this region
   *
   * If the region has more AZs than you want to use (for example, because of EIP limits),
   * pick a lower number here. The AZs will be sorted and picked from the start of the list.
   *
   * If you pick a higher number than the number of AZs in the region, all AZs in
   * the region will be selected. To use "all AZs" available to your account, use a
   * high number (such as 99).
   *
   * @default 3
   */
  maxAZs?: number;

  /**
   * The number of NAT Gateways to create.
   *
   * For example, if set this to 1 and your subnet configuration is for 3 Public subnets then only
   * one of the Public subnets will have a gateway and all Private subnets will route to this NAT Gateway.
   * @default maxAZs
   */
  natGateways?: number;

  /**
   * Configures the subnets which will have NAT Gateways
   *
   * You can pick a specific group of subnets by specifying the group name;
   * the picked subnets must be public subnets.
   *
   * @default All public subnets
   */
  natGatewayPlacement?: VpcPlacementStrategy;

  /**
   * Configure the subnets to build for each AZ
   *
   * The subnets are constructed in the context of the VPC so you only need
   * specify the configuration. The VPC details (VPC ID, specific CIDR,
   * specific AZ will be calculated during creation)
   *
   * For example if you want 1 public subnet, 1 private subnet, and 1 isolated
   * subnet in each AZ provide the following:
   * subnetConfiguration: [
   *    {
   *      cidrMask: 24,
   *      name: 'ingress',
   *      subnetType: SubnetType.Public,
   *    },
   *    {
   *      cidrMask: 24,
   *      name: 'application',
   *      subnetType: SubnetType.Private,
   *    },
   *    {
   *      cidrMask: 28,
   *      name: 'rds',
   *      subnetType: SubnetType.Isolated,
   *    }
   * ]
   *
   * `cidrMask` is optional and if not provided the IP space in the VPC will be
   * evenly divided between the requested subnets.
   *
   * @default the VPC CIDR will be evenly divided between 1 public and 1
   * private subnet per AZ
   */
  subnetConfiguration?: SubnetConfiguration[];
}


/**
 * Specify configuration parameters for a VPC to be built
 */
export interface SubnetConfiguration {
  /**
   * The CIDR Mask or the number of leading 1 bits in the routing mask
   *
   * Valid values are 16 - 28
   */
  cidrMask?: number;

  /**
   * The type of Subnet to configure.
   *
   * The Subnet type will control the ability to route and connect to the
   * Internet.
   */
  subnetType: SubnetType;

  /**
   * The common Logical Name for the `VpcSubnet`
   *
   * Thi name will be suffixed with an integer correlating to a specific
   * availability zone.
   */
  name: string;
}


/**
 * Specify configuration parameters for a VPC subnet
 */
export interface VpcSubnetProps {

  /**
   * The availability zone for the subnet
   */
  availabilityZone: string;

  /**
   * The VPC which this subnet is part of
   */
  vpcId: string;

  /**
   * The CIDR notation for this subnet
   */
  cidrBlock: string;

  /**
   * Controls if a public IP is associated to an instance at launch
   *
   * Defaults to true in Subnet.Public, false in Subnet.Private or Subnet.Isolated.
   */
  mapPublicIpOnLaunch?: boolean;
}


/**
 * The default tenancy of instances launched into the VPC.
 */
export enum DefaultInstanceTenancy {
  /**
   * Instances can be launched with any tenancy.
   */
  Default = 'default',

  /**
   * Any instance launched into the VPC automatically has dedicated tenancy, unless you launch it with the default tenancy.
   */
  Dedicated = 'dedicated'
}


/**
   * Import an exported VPC
   */
public static import(scope: cdk.Construct, id: string, props: VpcNetworkImportProps): IVpcNetwork {
    return new ImportedVpcNetwork(scope, id, props);
  }


/**
   * Import an existing VPC from context
   */
public static importFromContext(scope: cdk.Construct, id: string, props: VpcNetworkProviderProps): IVpcNetwork {
    return VpcNetwork.import(scope, id, new VpcNetworkProvider(scope, props).vpcProps);
  }


/**
   * Export this VPC from the stack
   */
public export(): VpcNetworkImportProps {
    const pub = new ExportSubnetGroup(this, 'PublicSubnetIDs', this.publicSubnets, SubnetType.Public, this.availabilityZones.length);
    const priv = new ExportSubnetGroup(this, 'PrivateSubnetIDs', this.privateSubnets, SubnetType.Private, this.availabilityZones.length);
    const iso = new ExportSubnetGroup(this, 'IsolatedSubnetIDs', this.isolatedSubnets, SubnetType.Isolated, this.availabilityZones.length);

    return {
      vpcId: new cdk.Output(this, 'VpcId', { value: this.vpcId }).makeImportValue().toString(),
      availabilityZones: this.availabilityZones,
      publicSubnetIds: pub.ids,
      publicSubnetNames: pub.names,
      privateSubnetIds: priv.ids,
      privateSubnetNames: priv.names,
      isolatedSubnetIds: iso.ids,
      isolatedSubnetNames: iso.names,
    };
  }


private createNatGateways(gateways?: number, placement?: VpcPlacementStrategy): void {
    const useNatGateway = this.subnetConfiguration.filter(
      subnet => (subnet.subnetType === SubnetType.Private)).length > 0;

    const natCount = ifUndefined(gateways,
      useNatGateway ? this.availabilityZones.length : 0);

    let natSubnets: VpcPublicSubnet[];
    if (placement) {
      const subnets = this.subnets(placement);
      for (const sub of subnets) {
        if (!this.isPublicSubnet(sub)) {
          throw new Error(`natGatewayPlacement ${placement} contains non public subnet ${sub}`);
        }
      }
      natSubnets = subnets as VpcPublicSubnet[];
    } else {
      natSubnets =  this.publicSubnets as VpcPublicSubnet[];
    }

    natSubnets = natSubnets.slice(0, natCount);
    for (const sub of natSubnets) {
      const gateway = sub.addNatGateway();
      this.natGatewayByAZ[sub.availabilityZone] = gateway.natGatewayId;
      this.natDependencies.push(gateway);
    }
  }


/**
   * createSubnets creates the subnets specified by the subnet configuration
   * array or creates the `DEFAULT_SUBNETS` configuration
   */
private createSubnets() {
    const remainingSpaceSubnets: SubnetConfiguration[] = [];

    // Calculate number of public/private subnets based on number of AZs
    const zones = new cdk.AvailabilityZoneProvider(this).availabilityZones;
    zones.sort();

    for (const subnet of this.subnetConfiguration) {
      if (subnet.cidrMask === undefined) {
        remainingSpaceSubnets.push(subnet);
        continue;
      }
      this.createSubnetResources(subnet, subnet.cidrMask);
    }

    const totalRemaining = remainingSpaceSubnets.length * this.availabilityZones.length;
    const cidrMaskForRemaing = this.networkBuilder.maskForRemainingSubnets(totalRemaining);
    for (const subnet of remainingSpaceSubnets) {
      this.createSubnetResources(subnet, cidrMaskForRemaing);
    }
  }


private createSubnetResources(subnetConfig: SubnetConfiguration, cidrMask: number) {
    this.availabilityZones.forEach((zone, index) => {
      const name = subnetId(subnetConfig.name, index);
      const subnetProps: VpcSubnetProps = {
        availabilityZone: zone,
        vpcId: this.vpcId,
        cidrBlock: this.networkBuilder.addSubnet(cidrMask),
        mapPublicIpOnLaunch: (subnetConfig.subnetType === SubnetType.Public),
      };

      let subnet: VpcSubnet;
      switch (subnetConfig.subnetType) {
        case SubnetType.Public:
          const publicSubnet = new VpcPublicSubnet(this, name, subnetProps);
          this.publicSubnets.push(publicSubnet);
          subnet = publicSubnet;
          break;
        case SubnetType.Private:
          const privateSubnet = new VpcPrivateSubnet(this, name, subnetProps);
          this.privateSubnets.push(privateSubnet);
          subnet = privateSubnet;
          break;
        case SubnetType.Isolated:
          const isolatedSubnet = new VpcPrivateSubnet(this, name, subnetProps);
          this.isolatedSubnets.push(isolatedSubnet);
          subnet = isolatedSubnet;
          break;
        default:
          throw new Error(`Unrecognized subnet type: ${subnetConfig.subnetType}`);
      }

      // These values will be used to recover the config upon provider import
      const includeResourceTypes = [CfnSubnet.resourceTypeName];
      subnet.node.apply(new cdk.Tag(SUBNETNAME_TAG, subnetConfig.name, {includeResourceTypes}));
      subnet.node.apply(new cdk.Tag(SUBNETTYPE_TAG, subnetTypeTagValue(subnetConfig.subnetType), {includeResourceTypes}));
    });
  }


public static import(scope: cdk.Construct, id: string, props: VpcSubnetImportProps): IVpcSubnet {
    return new ImportedVpcSubnet(scope, id, props);
  }


public export(): VpcSubnetImportProps {
    return {
      availabilityZone: new cdk.Output(this, 'AvailabilityZone', { value: this.availabilityZone }).makeImportValue().toString(),
      subnetId: new cdk.Output(this, 'VpcSubnetId', { value: this.subnetId }).makeImportValue().toString(),
    };
  }


protected addDefaultRouteToNAT(natGatewayId: string) {
    const route = new CfnRoute(this, `DefaultRoute`, {
      routeTableId: this.routeTableId,
      destinationCidrBlock: '0.0.0.0/0',
      natGatewayId
    });
    this.internetDependencies.add(route);
  }


/**
   * Create a default route that points to a passed IGW, with a dependency
   * on the IGW's attachment to the VPC.
   */
protected addDefaultRouteToIGW(
    gateway: CfnInternetGateway,
    gatewayAttachment: CfnVPCGatewayAttachment) {
    const route = new CfnRoute(this, `DefaultRoute`, {
      routeTableId: this.routeTableId,
      destinationCidrBlock: '0.0.0.0/0',
      gatewayId: gateway.ref
    });
    route.node.addDependency(gatewayAttachment);

    // Since the 'route' depends on the gateway attachment, just
    // depending on the route is enough.
    this.internetDependencies.add(route);
  }


/**
   * Create a default route that points to a passed IGW, with a dependency
   * on the IGW's attachment to the VPC.
   */
public addDefaultIGWRouteEntry(
    gateway: CfnInternetGateway,
    gatewayAttachment: CfnVPCGatewayAttachment) {
    this.addDefaultRouteToIGW(gateway, gatewayAttachment);
  }


/**
   * Creates a new managed NAT gateway attached to this public subnet.
   * Also adds the EIP for the managed NAT.
   * @returns A ref to the the NAT Gateway ID
   */
public addNatGateway() {
    // Create a NAT Gateway in this public subnet
    const ngw = new CfnNatGateway(this, `NATGateway`, {
      subnetId: this.subnetId,
      allocationId: new CfnEIP(this, `EIP`, {
        domain: 'vpc'
      }).eipAllocationId,
    });
    return ngw;
  }


/**
   * Adds an entry to this subnets route table that points to the passed NATGatwayId
   */
public addDefaultNatRouteEntry(natGatewayId: string) {
    this.addDefaultRouteToNAT(natGatewayId);
  }


public export() {
    return this.props;
  }


/** Provides operations to call the restore method. */
export class RestoreRequestBuilder {
    /** Path parameters for the request */
    private readonly pathParameters: Record<string, unknown>;
    /** The request adapter to use to execute the requests. */
    private readonly requestAdapter: RequestAdapter;
    /** Url template to use to build the URL for the current request builder */
    private readonly urlTemplate: string;
    /**
     * Instantiates a new RestoreRequestBuilder and sets the default values.
     * @param pathParameters The raw url or the Url template parameters for the request.
     * @param requestAdapter The request adapter to use to execute the requests.
     */
    public constructor(pathParameters: Record<string, unknown> | string | undefined, requestAdapter: RequestAdapter) {
        if(!pathParameters) throw new Error("pathParameters cannot be undefined");
        if(!requestAdapter) throw new Error("requestAdapter cannot be undefined");
        this.urlTemplate = "{+baseurl}/directoryRoles/{directoryRole%2Did}/microsoft.graph.restore";
        const urlTplParams = getPathParameters(pathParameters);
        this.pathParameters = urlTplParams;
        this.requestAdapter = requestAdapter;
    };
    /**
     * Invoke action restore
     * @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
     * @returns a RequestInformation
     */
    public createPostRequestInformation(requestConfiguration?: RestoreRequestBuilderPostRequestConfiguration | undefined) : RequestInformation {
        const requestInfo = new RequestInformation();
        requestInfo.urlTemplate = this.urlTemplate;
        requestInfo.pathParameters = this.pathParameters;
        requestInfo.httpMethod = HttpMethod.POST;
        if (requestConfiguration) {
            requestInfo.addRequestHeaders(requestConfiguration.headers);
            requestInfo.addRequestOptions(requestConfiguration.options);
        }
        return requestInfo;
    };
    /**
     * Invoke action restore
     * @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
     * @param responseHandler Response handler to use in place of the default response handling provided by the core service
     * @returns a Promise of DirectoryObject
     */
    public post(requestConfiguration?: RestoreRequestBuilderPostRequestConfiguration | undefined, responseHandler?: ResponseHandler | undefined) : Promise<DirectoryObject | undefined> {
        const requestInfo = this.createPostRequestInformation(
            requestConfiguration
        );
        return this.requestAdapter?.sendAsync<DirectoryObject>(requestInfo, createDirectoryObjectFromDiscriminatorValue, responseHandler, undefined) ?? Promise.reject(new Error('http core is null'));
    };
}


/**
     * Invoke action restore
     * @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
     * @returns a RequestInformation
     */
public createPostRequestInformation(requestConfiguration?: RestoreRequestBuilderPostRequestConfiguration | undefined) : RequestInformation {
        const requestInfo = new RequestInformation();
        requestInfo.urlTemplate = this.urlTemplate;
        requestInfo.pathParameters = this.pathParameters;
        requestInfo.httpMethod = HttpMethod.POST;
        if (requestConfiguration) {
            requestInfo.addRequestHeaders(requestConfiguration.headers);
            requestInfo.addRequestOptions(requestConfiguration.options);
        }
        return requestInfo;
    }


/**
     * Invoke action restore
     * @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
     * @param responseHandler Response handler to use in place of the default response handling provided by the core service
     * @returns a Promise of DirectoryObject
     */
public post(requestConfiguration?: RestoreRequestBuilderPostRequestConfiguration | undefined, responseHandler?: ResponseHandler | undefined) : Promise<DirectoryObject | undefined> {
        const requestInfo = this.createPostRequestInformation(
            requestConfiguration
        );
        return this.requestAdapter?.sendAsync<DirectoryObject>(requestInfo, createDirectoryObjectFromDiscriminatorValue, responseHandler, undefined) ?? Promise.reject(new Error('http core is null'));
    }


(index: number, _piece: PieceProps) => {
    if (Math.floor(index / 8 + index) % 2) {
      const [color, setColor] = React.useState("black");
      const [piece, setPiece] = React.useState(_piece);
      return {
        piece: piece,
        setPiece: setPiece,
        color: color,
        setColor: setColor,
      };
    } else {
      const [color, setColor] = React.useState("white");
      const [piece, setPiece] = React.useState(_piece);
      return {
        piece: piece,
        setPiece: setPiece,
        color: color,
        setColor: setColor,
      };
    }
  }


() => {
    let BoardConfig: Array<BoardStatusProps> = [];
    const white_row = [
      PieceDetails.WHITE_ROOK,
      PieceDetails.WHITE_KNIGHT,
      PieceDetails.WHITE_BISHOP,
      PieceDetails.WHITE_QUEEN,
      PieceDetails.WHITE_KING,
      PieceDetails.WHITE_BISHOP,
      PieceDetails.WHITE_KNIGHT,
      PieceDetails.WHITE_ROOK,
    ];
    const black_row = [
      PieceDetails.BLACK_ROOK,
      PieceDetails.BLACK_KNIGHT,
      PieceDetails.BLACK_BISHOP,
      PieceDetails.BLACK_QUEEN,
      PieceDetails.BLACK_KING,
      PieceDetails.BLACK_BISHOP,
      PieceDetails.BLACK_KNIGHT,
      PieceDetails.BLACK_ROOK,
    ];

    // rnbqkbnr
    for (let index = 0; index < 8; index++) {
      const _piece = utils.getNewPiece("black-piece", black_row[index], index);
      BoardConfig = [...BoardConfig, updateBoardConfig(index, _piece)];
    }
    // pppppppp
    for (let index = 8; index < 16; index++) {
      const _piece = utils.getNewPiece("black-piece", PieceDetails.BLACK_PAWN, index);
      BoardConfig = [...BoardConfig, updateBoardConfig(index, _piece)];
    }
    // 8/8/8/8
    for (let index = 16; index < 48; index++) {
      const _piece = utils.getEmptyCell(index);
      BoardConfig = [...BoardConfig, updateBoardConfig(index, _piece)];
    }
    // PPPPPPPP
    for (let index = 48; index < 56; index++) {
      const _piece = utils.getNewPiece("white-piece", PieceDetails.WHITE_PAWN, index);
      BoardConfig = [...BoardConfig, updateBoardConfig(index, _piece)];
    }
    // RNBQKBNR
    for (let index = 56; index < 64; index++) {
      const _piece = utils.getNewPiece("white-piece", white_row[index % 8], index);
      BoardConfig = [...BoardConfig, updateBoardConfig(index, _piece)];
    }

    return BoardConfig;
  }


() => {
    socket.once("updateMoveTable", (data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("+++ updateMoveTable +++", data);
      }
      setGameMoves((gameMoves) => [...gameMoves, data.move]);
      if (data.checkmate || data.stalemate) {
        socket.emit("game-complete", {
          result: data.result,
          gameCode: gameCode,
          gameMoves: [...gameMoves, data.move],
        });
      }
    });
  }


(data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("+++ updateMoveTable +++", data);
      }
      setGameMoves((gameMoves) => [...gameMoves, data.move]);
      if (data.checkmate || data.stalemate) {
        socket.emit("game-complete", {
          result: data.result,
          gameCode: gameCode,
          gameMoves: [...gameMoves, data.move],
        });
      }
    }


(gameMoves) => [...gameMoves, data.move]


() => {
    socket.once("nextTurn", (data) => {
      if (process.env.NODE_ENV === "development") {
        console.log(socket.id === data.socket ? "+++ moveSelf +++" : "+++ moveOpponent +++");
      }
      const [from, to, moveType, sockId] = [data.fromPiece, data.toPiece, data.moveType, data.socket];
      if (BoardConfig[data.fromPos].piece !== data.fromPiece) {
        BoardConfig[data.fromPos].setPiece(data.fromPiece);
      }
      if (BoardConfig[data.toPos].piece !== data.toPiece) {
        BoardConfig[data.toPos].setPiece(data.toPiece);
      }
      if (clickedPiece !== dummyPiece) {
        updateClickedPiece(dummyPiece);
      }
      if (sockId === socket.id && from.position !== -1 && to.position !== -1) {
        getCheckStatus(from, to, moveType);
      }
      setCurrentTurn(currentTurn === "white" ? "black" : "white");
      updateScores(data.toPiece, data.points);
    });
    return () => {
      socket.off("nextTurn");
    };
  }


(data) => {
      if (process.env.NODE_ENV === "development") {
        console.log(socket.id === data.socket ? "+++ moveSelf +++" : "+++ moveOpponent +++");
      }
      const [from, to, moveType, sockId] = [data.fromPiece, data.toPiece, data.moveType, data.socket];
      if (BoardConfig[data.fromPos].piece !== data.fromPiece) {
        BoardConfig[data.fromPos].setPiece(data.fromPiece);
      }
      if (BoardConfig[data.toPos].piece !== data.toPiece) {
        BoardConfig[data.toPos].setPiece(data.toPiece);
      }
      if (clickedPiece !== dummyPiece) {
        updateClickedPiece(dummyPiece);
      }
      if (sockId === socket.id && from.position !== -1 && to.position !== -1) {
        getCheckStatus(from, to, moveType);
      }
      setCurrentTurn(currentTurn === "white" ? "black" : "white");
      updateScores(data.toPiece, data.points);
    }


() => {
      socket.off("nextTurn");
    }


() => {
    socket.once("performCastling", (data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("+++ performCastling +++", data);
      }
      const [from, to, sockId] = [data.newRookPiece, data.newKingPiece, data.socket];
      if (BoardConfig[data.oldKingPos].piece !== data.oldKingPiece) {
        BoardConfig[data.oldKingPos].setPiece(data.oldKingPiece);
      }
      if (BoardConfig[data.newKingPos].piece !== data.newKingPiece) {
        BoardConfig[data.newKingPos].setPiece(data.newKingPiece);
      }
      if (BoardConfig[data.oldRookPos].piece !== data.oldRookPiece) {
        BoardConfig[data.oldRookPos].setPiece(data.oldRookPiece);
      }
      if (BoardConfig[data.newRookPos].piece !== data.newRookPiece) {
        BoardConfig[data.newRookPos].setPiece(data.newRookPiece);
      }
      if (clickedPiece !== dummyPiece) {
        updateClickedPiece(dummyPiece);
      }
      if (sockId === socket.id && from.position !== -1 && to.position !== -1) {
        getCheckStatus(from, to, MoveTypes.CASTLE);
      }
      setCurrentTurn(currentTurn === "white" ? "black" : "white");
    });
    return () => {
      socket.off("performCastling");
    };
  }


(data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("+++ performCastling +++", data);
      }
      const [from, to, sockId] = [data.newRookPiece, data.newKingPiece, data.socket];
      if (BoardConfig[data.oldKingPos].piece !== data.oldKingPiece) {
        BoardConfig[data.oldKingPos].setPiece(data.oldKingPiece);
      }
      if (BoardConfig[data.newKingPos].piece !== data.newKingPiece) {
        BoardConfig[data.newKingPos].setPiece(data.newKingPiece);
      }
      if (BoardConfig[data.oldRookPos].piece !== data.oldRookPiece) {
        BoardConfig[data.oldRookPos].setPiece(data.oldRookPiece);
      }
      if (BoardConfig[data.newRookPos].piece !== data.newRookPiece) {
        BoardConfig[data.newRookPos].setPiece(data.newRookPiece);
      }
      if (clickedPiece !== dummyPiece) {
        updateClickedPiece(dummyPiece);
      }
      if (sockId === socket.id && from.position !== -1 && to.position !== -1) {
        getCheckStatus(from, to, MoveTypes.CASTLE);
      }
      setCurrentTurn(currentTurn === "white" ? "black" : "white");
    }


() => {
      socket.off("performCastling");
    }


() => {
    socket.once("performPromotion", (data) => {
      const [from, to, sockId] = [data.oldPiece, data.newPiece, data.socket];
      if (BoardConfig[data.oldPiecePos].piece !== data.oldPiece) {
        BoardConfig[data.oldPiecePos].setPiece(data.oldPiece);
      }
      if (BoardConfig[data.newPiecePos].piece !== data.newPiece) {
        BoardConfig[data.newPiecePos].setPiece(data.newPiece);
      }
      if (clickedPiece !== dummyPiece) {
        updateClickedPiece(dummyPiece);
      }
      if (sockId === socket.id && from.position !== -1 && to.position !== -1) {
        getCheckStatus(from, to, MoveTypes.PROMOTION);
      }
      setCurrentTurn(currentTurn === "white" ? "black" : "white");
    });
    return () => {
      socket.off("performPromotion");
    };
  }


(data) => {
      const [from, to, sockId] = [data.oldPiece, data.newPiece, data.socket];
      if (BoardConfig[data.oldPiecePos].piece !== data.oldPiece) {
        BoardConfig[data.oldPiecePos].setPiece(data.oldPiece);
      }
      if (BoardConfig[data.newPiecePos].piece !== data.newPiece) {
        BoardConfig[data.newPiecePos].setPiece(data.newPiece);
      }
      if (clickedPiece !== dummyPiece) {
        updateClickedPiece(dummyPiece);
      }
      if (sockId === socket.id && from.position !== -1 && to.position !== -1) {
        getCheckStatus(from, to, MoveTypes.PROMOTION);
      }
      setCurrentTurn(currentTurn === "white" ? "black" : "white");
    }


() => {
      socket.off("performPromotion");
    }


() => {
    socket.once("markCheck", (data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("King in check", data);
      }
      BoardConfig[data.position].setColor(data.color);
      pieceInCheck = data.position;
    });
    return () => {
      socket.off("markCheck");
    };
  }


(data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("King in check", data);
      }
      BoardConfig[data.position].setColor(data.color);
      pieceInCheck = data.position;
    }


() => {
      socket.off("markCheck");
    }


() => {
    socket.once("unmarkCheck", (data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("King avoided check", data);
      }
      BoardConfig[data.position].setColor(data.color);
      pieceInCheck = -1;
    });
    return () => {
      socket.off("unmarkCheck");
    };
  }


(data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("King avoided check", data);
      }
      BoardConfig[data.position].setColor(data.color);
      pieceInCheck = -1;
    }


() => {
      socket.off("unmarkCheck");
    }


() => {
    socket.once("gameComplete", (data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("+++ gameComplete +++", data);
      }
      setGameComplete(true);
      const message = data.result.outcome + " by " + data.result.message;
      setGameResult(message);
    });
    return () => {
      socket.off("gameComplete");
    };
  }


(data) => {
      if (process.env.NODE_ENV === "development") {
        console.log("+++ gameComplete +++", data);
      }
      setGameComplete(true);
      const message = data.result.outcome + " by " + data.result.message;
      setGameResult(message);
    }


() => {
      socket.off("gameComplete");
    }


(from: PieceProps, value: number) => {
    if (utils.getPieceColor(from) === "white") {
      setWhitePoints(whitePoints + value);
    } else {
      setBlackPoints(blackPoints + value);
    }
  }


(
    from: PieceProps,
    to: PieceProps,
    moveType: number,
    isCheck: boolean,
    isCheckMate: boolean
  ) => {
    const posTo = to.position;
    const [x, y] = [(8 - Math.floor(posTo / 8)).toString(), String.fromCharCode(97 + (posTo % 8))];
    let moveRep = "";
    if (moveType === 2) {
      moveRep = from.position < to.position ? "0-0" : "0-0-0";
    } else {
      if (moveType === 3) {
        moveRep = y + x + "=" + to.identifier;
      } else {
        moveRep = to.identifier + (moveType === 1 ? "x" : "") + y + x;
      }
    }
    if (isCheckMate) {
      moveRep += "#";
    } else if (isCheck) {
      moveRep += "+";
    }
    // setGameMoves([...gameMoves, moveRep]);
    return moveRep;
  }


(kingPos: number, oppMoves: number[], selfMoves: number[], attacks: number[]) => {
    const moves = new Hints(BoardConfig);
    const ischeckMate = moves.isCheckMate(kingPos, oppMoves, selfMoves, attacks);
    return ischeckMate;
  }


(piece: PieceProps): [boolean, boolean] => {
    const moves = new Hints(BoardConfig);
    const outVal = moves.isCheck(utils.getPieceColor(piece));
    if (outVal.attackingPieces.length > 0) {
      socket.emit("setCheck", {
        gameCode: gameCode,
        position: outVal.oppKingPos,
        color: "check",
      });
      const ischeckMate = isCheckMate(
        outVal.oppKingPos,
        outVal.selfPossibleMoves,
        outVal.oppPossibleMoves,
        outVal.attackingPieces
      );
      return [true, ischeckMate];
    } else {
      if (pieceInCheck !== -1) {
        socket.emit("unsetCheck", {
          gameCode: gameCode,
          position: pieceInCheck,
          color: Math.floor(pieceInCheck / 8 + pieceInCheck) % 2 ? "black" : "white",
        });
      }
      return [false, false];
    }
  }


(piece: PieceProps) => {
    const moves = new Hints(BoardConfig);
    return moves.isStaleMate(utils.getPieceColor(piece));
  }


(from: PieceProps, to: PieceProps): boolean => {
    let possible: boolean = true;
    possible &&= utils.getPieceName(from) === "pawn";
    let rank = Math.floor(to.position / 8);
    possible &&= rank == 0 || rank == 7;
    return possible;
  }


(from: PieceProps, to: PieceProps) => {
    const [posFrom, posTo] = [from.position, to.position];
    if (process.env.NODE_ENV === "development") {
      console.log(`Making a move from ${posFrom} to ${posTo}`);
    }
    to.position = posFrom;
    from.position = posTo;
    from.numMoves += 1;

    socket.emit("perform-move", {
      fromPos: posFrom,
      fromPiece: to,
      toPos: posTo,
      toPiece: from,
      gameCode: gameCode,
      points: 0,
      moveType: MoveTypes.MOVE,
    });
  }


(from: PieceProps, to: PieceProps) => {
    const [posFrom, posTo] = [from.position, to.position];
    if (process.env.NODE_ENV === "development") {
      console.log(`Making a capture from ${posFrom} to ${posTo}`);
    }
    const emptyCell = utils.getEmptyCell(posFrom);
    from.position = posTo;
    to.position = posFrom;
    from.numMoves += 1;

    socket.emit("perform-move", {
      fromPos: posFrom,
      fromPiece: emptyCell,
      toPos: posTo,
      toPiece: from,
      gameCode: gameCode,
      points: to.value,
      moveType: MoveTypes.CAPTURE,
    });
  }


(from: PieceProps, to: PieceProps) => {
    const newPos = [from.position, to.position];
    const color = utils.getPieceColor(from);
    setPromotionPos([...newPos]);
    setPawnPromotionType(color + "-promotion");
  }


(from: PieceProps, to: PieceProps) => {
    const [posFrom, posTo] = [from.position, to.position];
    const emptyCell0 = utils.getEmptyCell(posFrom);
    const emptyCell1 = utils.getEmptyCell(posTo);
    let [newKingPos, newRookPos] = [posFrom, posTo];
    if (posFrom < posTo) {
      // kingSide
      newKingPos += 2;
      newRookPos -= 2;
    } else {
      // queenSide
      newKingPos -= 2;
      newRookPos += 3;
    }
    from.position = newKingPos;
    from.numMoves += 1;
    to.position = newRookPos;
    to.numMoves += 1;

    socket.emit("castle", {
      oldKingPiece: emptyCell0,
      newKingPiece: from,
      oldKingPos: posFrom,
      newKingPos: newKingPos,
      oldRookPiece: emptyCell1,
      newRookPiece: to,
      oldRookPos: posTo,
      newRookPos: newRookPos,
      gameCode: gameCode,
    });
  }


(from: PieceProps, to: PieceProps): number => {
    if (checkPromotion(from, to)) {
      performPromotion(from, to);
      return MoveTypes.PROMOTION;
    } else if (to.pieceName === null) {
      performMove(from, to);
      return MoveTypes.MOVE;
    } else if (utils.getPieceColor(to) === utils.getPieceColor(from)) {
      performCastling(from, to);
      return MoveTypes.CASTLE;
    } else {
      performCapture(from, to);
      return MoveTypes.CAPTURE;
    }
  }


(piece: PieceProps): boolean => {
    const index = piece.position;
    return piece.position !== clickedPiece.position && BoardConfig[index].color === "selected";
  }


(fromPiece: PieceProps, toPiece: PieceProps, moveType: number) => {
    const color = utils.getPieceColor(toPiece);
    BoardConfig[fromPiece.position].piece = fromPiece;
    BoardConfig[toPiece.position].piece = toPiece;
    if (process.env.NODE_ENV === "development") {
      console.log("Check Status", fromPiece, toPiece, moveType);
    }

    let result: Result = { outcome: "", message: "" };

    const [ischeck, ischeckmate] = isCheck(toPiece);
    const isstalemate = isStalemate(toPiece);
    const lastMove = getMoveRepresentation(fromPiece, toPiece, moveType, ischeck, ischeckmate);

    if (ischeckmate) {
      result.outcome = color === "white" ? GameResultTypes.WHITE : GameResultTypes.BLACK;
      result.message = "Checkmate";
    }

    socket.emit("getMoveRepresentation", {
      move: lastMove,
      gameCode: gameCode,
      result: result,
      checkmate: ischeckmate,
      stalemate: isstalemate,
    });
  }


(piece: PieceProps) => {
    const moves = new Hints(BoardConfig);
    if (checkPossibleMove(piece)) {
      const moveType = makeMove(clickedPiece, piece);
      moves.hideHints(hintCells);
    } else {
      const color = utils.getPieceColor(piece);
      if (color === currentTurn && color == playerColor) {
        updateClickedPiece(piece);
        moves.hideHints(hintCells);
        const validMoves = moves.showHints(piece);
        updateHintCells(validMoves);
      }
    }
  }


() => {
    const squares: any = [];
    for (let index = 0; index < 64; index++) {
      squares.push(
        <Square
          key={`square_${index}`}
          color={BoardConfig[index].color}
          position={index}
          piece={BoardConfig[index].piece}
          onClick={squareOnClickHandler}
        />


export interface Result {
  outcome: string;
  message: string;
}


export interface BoardStatusProps {
  piece: PieceProps /** The piece under consideration */;
  setPiece: React.Dispatch<React.SetStateAction<PieceProps>> /** The callback to update the above `piece` */;
  color: string /** The color of the board cell under consideration */;
  setColor: React.Dispatch<React.SetStateAction<string>> /** The callback to update the above `color` */;
}


export interface BoardProps {
  currentTurn: string /** State representing the current turn, 'white' or 'black' */;
  setCurrentTurn: React.Dispatch<React.SetStateAction<string>>;
  /** The callback to update the above `currentTurn` */
  whitePoints: number /** State representing the points scored by the player with white pieces */;
  setWhitePoints: React.Dispatch<React.SetStateAction<number>>;
  /** The callback to update the above `whitePoints` */
  blackPoints: number /** State representing the points scored by the player with black pieces */;
  setBlackPoints: React.Dispatch<React.SetStateAction<number>>;
  /** The callback to update the above `blackPoints` */
  gameMoves: Array<string> /** State representing the moves in the game so far */;
  setGameMoves: React.Dispatch<React.SetStateAction<string[]>> /** The callback to update the moves in the game */;
  /** The socket for the current player */
  socket: Socket<DefaultEventsMap, DefaultEventsMap>;
  gameCode: string /** The gameCode for the current Game */;
  playerColor: string /** The piece color current player is playing on */;
}


enum MoveTypes {
  MOVE = 0,
  CAPTURE = 1,
  CASTLE = 2,
  PROMOTION = 3,
}


export enum GameResultTypes {
  WHITE = "White Won",
  BLACK = "Black Won",
  DRAW = "Game Drawn",
}


renderSquares()


() => {
      if (auth.isAuthenticated()) {
        this.rootPage = SearchPage;
        menu.enable(true, 'profile-menu');
      } else {
        this.rootPage = LoginPage;
        menu.enable(false, 'profile-menu');
      }
      setTimeout(() => splashScreen.hide(), 500);
    }


() => splashScreen.hide()


(err) => { // Catch any errors that occured.
      console.log(err);
    }


() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      statusBar.styleLightContent();
      headerColor.tint('#0052cc');
    }


@Component({
  templateUrl: 'app.html'
})
export class App {
  @ViewChild('mycontent') nav: NavController
  rootPage: any;

  user: User;

  constructor(
    platform: Platform,
    statusBar: StatusBar,
    splashScreen: SplashScreen,
    headerColor: HeaderColor,
    private auth: AuthProvider,
    private menu: MenuController,
    public events: Events
  ) {

    // Load authString and stored user, assuming they exist in storage
    auth.loadFromStorage()
    .then(() => {
      if (auth.isAuthenticated()) {
        this.rootPage = SearchPage;
        menu.enable(true, 'profile-menu');
      } else {
        this.rootPage = LoginPage;
        menu.enable(false, 'profile-menu');
      }
      setTimeout(() => splashScreen.hide(), 500);
    })
    .catch((err) => { // Catch any errors that occured.
      console.log(err);
    })

    platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      statusBar.styleLightContent();
      headerColor.tint('#0052cc');
    });

    // Update the menu with any changes in user information
    this.events.subscribe('user:set', (user) => {
      this.user = user;
    })
  }

  // Disable the menu, destroy the auth, and go back to login
  public onLogoutSubmit(): void {
    this.menu.enable(false, 'profile-menu');
    this.auth.destroyAuth();
    this.nav.setRoot(LoginPage,{},{animate: true, direction: 'back'});
  }
}


// Disable the menu, destroy the auth, and go back to login
public onLogoutSubmit(): void {
    this.menu.enable(false, 'profile-menu');
    this.auth.destroyAuth();
    this.nav.setRoot(LoginPage,{},{animate: true, direction: 'back'});
  }


export interface Config {
    kind: 'Bootstrap' | 'Generator' | 'Module' | 'MethodDelegate' | 'DelegateFactory';
    id: string;
    apiVersion?: string;
}


(err) => {
      if (err) {
        console.log(
          "Failed to deliver " +
            request.toString() +
            ", err: " +
            JSON.stringify(err)
        );
      }
    }


(err, response) => {
      if (err) {
        console.log("Emulator shutdown failed", JSON.stringify(err));
      }
    }


(img: Image) => {
      // Make sure we properly translate mouse clicks.
      const format = img.getFormat()!;
      this.width = format.getWidth();
      this.height = format.getHeight();
      this.emitter.emit("frame", img);
    }


(err: any) => {
      // We cancel the stream on resize, so lets restart it!
      console.log("Error from screenshot: " + err.message);
      switch (err.code) {
        case 1:
          if (this.wantsResize) {
            this.wantsResize = false;
            this.streamScreenshot();
          }
          break;
        case 13:
          this.close();
          break;
        default:
          console.log("Error from screenshot: " + JSON.stringify(err));
      }

      // Remove our transport file.
      fs.unlinkSync(tmpfile);
    }