() => {
      const frame = toDataFrame({
        name: 'temperatures',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'temperature.indoor', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      const suggestions = getDataFrameVars([frame]);

      expect(suggestions).toEqual([
        {
          value: '__data.fields.time',
          label: 'time',
          documentation: `Formatted value for time on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: '__data.fields["temperature.indoor"]',
          label: 'temperature.indoor',
          documentation: `Formatted value for temperature.indoor on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields[0]`,
          label: `Select by index`,
          documentation: `Enter the field order`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["temperature.indoor"].numeric`,
          label: `Show numeric value`,
          documentation: `the numeric field value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["temperature.indoor"].text`,
          label: `Show text value`,
          documentation: `the text value`,
          origin: VariableOrigin.Fields,
        },
      ]);
    }


() => {
    it('then it should return correct suggestions', () => {
      const frame = toDataFrame({
        name: 'temperatures',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'temperature.indoor', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      frame.fields[1].config = { ...frame.fields[1].config, displayName: 'Indoor Temperature' };

      const suggestions = getDataFrameVars([frame]);

      expect(suggestions).toEqual([
        {
          value: '__data.fields.time',
          label: 'time',
          documentation: `Formatted value for time on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: '__data.fields["Indoor Temperature"]',
          label: 'Indoor Temperature',
          documentation: `Formatted value for Indoor Temperature on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields[0]`,
          label: `Select by index`,
          documentation: `Enter the field order`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].numeric`,
          label: `Show numeric value`,
          documentation: `the numeric field value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].text`,
          label: `Show text value`,
          documentation: `the text value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"]`,
          label: `Select by title`,
          documentation: `Use the title to pick the field`,
          origin: VariableOrigin.Fields,
        },
      ]);
    });
  }


() => {
      const frame = toDataFrame({
        name: 'temperatures',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'temperature.indoor', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      frame.fields[1].config = { ...frame.fields[1].config, displayName: 'Indoor Temperature' };

      const suggestions = getDataFrameVars([frame]);

      expect(suggestions).toEqual([
        {
          value: '__data.fields.time',
          label: 'time',
          documentation: `Formatted value for time on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: '__data.fields["Indoor Temperature"]',
          label: 'Indoor Temperature',
          documentation: `Formatted value for Indoor Temperature on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields[0]`,
          label: `Select by index`,
          documentation: `Enter the field order`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].numeric`,
          label: `Show numeric value`,
          documentation: `the numeric field value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].text`,
          label: `Show text value`,
          documentation: `the text value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"]`,
          label: `Select by title`,
          documentation: `Use the title to pick the field`,
          origin: VariableOrigin.Fields,
        },
      ]);
    }


() => {
    it('then it should ignore duplicates', () => {
      const frame = toDataFrame({
        name: 'temperatures',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'temperature.indoor', type: FieldType.number, values: [10, 11, 12] },
          { name: 'temperature.outdoor', type: FieldType.number, values: [20, 21, 22] },
        ],
      });

      frame.fields[1].config = { ...frame.fields[1].config, displayName: 'Indoor Temperature' };
      // Someone makes a mistake when renaming a field
      frame.fields[2].config = { ...frame.fields[2].config, displayName: 'Indoor Temperature' };

      const suggestions = getDataFrameVars([frame]);

      expect(suggestions).toEqual([
        {
          value: '__data.fields.time',
          label: 'time',
          documentation: `Formatted value for time on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: '__data.fields["Indoor Temperature"]',
          label: 'Indoor Temperature',
          documentation: `Formatted value for Indoor Temperature on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields[0]`,
          label: `Select by index`,
          documentation: `Enter the field order`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].numeric`,
          label: `Show numeric value`,
          documentation: `the numeric field value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].text`,
          label: `Show text value`,
          documentation: `the text value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"]`,
          label: `Select by title`,
          documentation: `Use the title to pick the field`,
          origin: VariableOrigin.Fields,
        },
      ]);
    });
  }


() => {
      const frame = toDataFrame({
        name: 'temperatures',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'temperature.indoor', type: FieldType.number, values: [10, 11, 12] },
          { name: 'temperature.outdoor', type: FieldType.number, values: [20, 21, 22] },
        ],
      });

      frame.fields[1].config = { ...frame.fields[1].config, displayName: 'Indoor Temperature' };
      // Someone makes a mistake when renaming a field
      frame.fields[2].config = { ...frame.fields[2].config, displayName: 'Indoor Temperature' };

      const suggestions = getDataFrameVars([frame]);

      expect(suggestions).toEqual([
        {
          value: '__data.fields.time',
          label: 'time',
          documentation: `Formatted value for time on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: '__data.fields["Indoor Temperature"]',
          label: 'Indoor Temperature',
          documentation: `Formatted value for Indoor Temperature on the same row`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields[0]`,
          label: `Select by index`,
          documentation: `Enter the field order`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].numeric`,
          label: `Show numeric value`,
          documentation: `the numeric field value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"].text`,
          label: `Show text value`,
          documentation: `the text value`,
          origin: VariableOrigin.Fields,
        },
        {
          value: `__data.fields["Indoor Temperature"]`,
          label: `Select by title`,
          documentation: `Use the title to pick the field`,
          origin: VariableOrigin.Fields,
        },
      ]);
    }


() => {
    it('it should not return any suggestions', () => {
      const frame1 = toDataFrame({
        name: 'server1',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'value', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      const frame2 = toDataFrame({
        name: 'server2',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'value', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      const suggestions = getDataFrameVars([frame1, frame2]);

      expect(suggestions).toEqual([]);
    });
  }


() => {
      const frame1 = toDataFrame({
        name: 'server1',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'value', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      const frame2 = toDataFrame({
        name: 'server2',
        fields: [
          { name: 'time', type: FieldType.time, values: [1, 2, 3] },
          { name: 'value', type: FieldType.number, values: [10, 11, 12] },
        ],
      });

      const suggestions = getDataFrameVars([frame1, frame2]);

      expect(suggestions).toEqual([]);
    }


() => useDispatch<AppDispatch>()


() => {
  let content = <>
    <TwoPageBanner
      currentUri="contact"
    />

Paris, France

Gagny

                        {/*

00 (440) 9865 562

Mon to Fri 9am to 6 pm

 */}

contact@eazys-dev.fr

Toujours présent en cas de besoin.

                <ContactForm/>

  </>
  return content
}


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

	beforeEach(async(() => {
		TestBed.configureTestingModule({
			declarations: [VerifiedScreenComponent],
			imports: [
				TranslateModule.forRoot(),
				SharedMaterialModule,
				ToastrModule.forRoot(),
				RouterTestingModule,
				HttpClientModule,
			],
			providers: [UtilService],
		}).compileComponents();
	}));

	beforeEach(() => {
		fixture = TestBed.createComponent(VerifiedScreenComponent);
		component = fixture.componentInstance;
		fixture.detectChanges();
	});

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


() => {
		TestBed.configureTestingModule({
			declarations: [VerifiedScreenComponent],
			imports: [
				TranslateModule.forRoot(),
				SharedMaterialModule,
				ToastrModule.forRoot(),
				RouterTestingModule,
				HttpClientModule,
			],
			providers: [UtilService],
		}).compileComponents();
	}


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


(link, idx) => (
                    <React.Fragment key={idx}>
                        {FoundationDataHelper.buildLink(link.url, link.label)}


/**
 * Class to help with foundation data.
 */
export class FoundationDataHelper {
    /**
     * The location of the foundation data.
     */
    public static FOUNDATION_DATA_URL: string = "https://webassets.iota.org/data/foundation.json";

    /**
     * Cached version of the data.
     */
    private static _foundationData: IFoundationData;

    /**
     * Load the found data.
     * @returns The loaded foundation data.
     */
    public static async loadData(): Promise<IFoundationData | undefined> {
        if (!FoundationDataHelper._foundationData) {
            try {
                const foundationDataResponse = await fetch(FoundationDataHelper.FOUNDATION_DATA_URL);
                FoundationDataHelper._foundationData = await foundationDataResponse.json();
            } catch {
                // eslint-disable-next-line no-console
                console.error(`Failed loading foundation data from ${FoundationDataHelper.FOUNDATION_DATA_URL}`);
            }
        }

        return FoundationDataHelper._foundationData;
    }

    /**
     * Create the display for a value.
     * @param info The information to display.
     * @param info.label The label for the information.
     * @param info.value The optional value for the information.
     * @param info.urls The optional urls.
     * @param key The key of the item.
     * @returns The element to display.
     */
    public static createValue(
        info: {
            /**
             * The label for the information.
             */
            label: string;

            /**
             * The optional value for the information.
             */
            value?: string;

            /**
             * The optional urls.
             */
            urls?: {
                /**
                 * The label for the link.
                 */
                label: string;
                /**
                 * The url to link to.
                 */
                url: string;
            }[];
        },
        key?: Key): React.ReactNode {
        return (
            <React.Fragment key={key}>
                {info.label && (
                    <span
                        className="data-label"
                        dangerouslySetInnerHTML={
                            { __html: FoundationDataHelper.buildLines(info.label) }
                        }


/**
     * Load the found data.
     * @returns The loaded foundation data.
     */
public static async loadData(): Promise<IFoundationData | undefined> {
        if (!FoundationDataHelper._foundationData) {
            try {
                const foundationDataResponse = await fetch(FoundationDataHelper.FOUNDATION_DATA_URL);
                FoundationDataHelper._foundationData = await foundationDataResponse.json();
            } catch {
                // eslint-disable-next-line no-console
                console.error(`Failed loading foundation data from ${FoundationDataHelper.FOUNDATION_DATA_URL}`);
            }
        }

        return FoundationDataHelper._foundationData;
    }


/**
     * Create the display for a value.
     * @param info The information to display.
     * @param info.label The label for the information.
     * @param info.value The optional value for the information.
     * @param info.urls The optional urls.
     * @param key The key of the item.
     * @returns The element to display.
     */
public static createValue(
        info: {
            /**
             * The label for the information.
             */
            label: string;

            /**
             * The optional value for the information.
             */
            value?: string;

            /**
             * The optional urls.
             */
            urls?: {
                /**
                 * The label for the link.
                 */
                label: string;
                /**
                 * The url to link to.
                 */
                url: string;
            }[];
        },
        key?: Key): React.ReactNode {
        return (
            <React.Fragment key={key}>
                {info.label && (
                    <span
                        className="data-label"
                        dangerouslySetInnerHTML={
                            { __html: FoundationDataHelper.buildLines(info.label) }
                        }


() => {
  it("should call ParamFilter.useParam method with the correct parameters", () => {
    class Test {}

    class Ctrl {
      test(@Cookies("expression", Test) body: Test) {}
    }

    const param = ParamRegistry.get(Ctrl, "test", 0);
    param.expression.should.eq("expression");
    param.paramType.should.eq(ParamTypes.COOKIES);
    param.type.should.eq(Test);
  });
}


() => {
    class Test {}

    class Ctrl {
      test(@Cookies("expression", Test) body: Test) {}
    }

    const param = ParamRegistry.get(Ctrl, "test", 0);
    param.expression.should.eq("expression");
    param.paramType.should.eq(ParamTypes.COOKIES);
    param.type.should.eq(Test);
  }


class Test {}


class Ctrl {
      test(@Cookies("expression", Test) body: Test) {}
    }


test(@Cookies("expression", Test) body: Test) {}


(args: IconProps) => <ThreeDots {...args} />


/**
 * Mesh representing the gorund
 */
export declare class GroundMesh extends Mesh {
    /** If octree should be generated */
    generateOctree: boolean;
    private _heightQuads;
    /** @hidden */
    _subdivisionsX: number;
    /** @hidden */
    _subdivisionsY: number;
    /** @hidden */
    _width: number;
    /** @hidden */
    _height: number;
    /** @hidden */
    _minX: number;
    /** @hidden */
    _maxX: number;
    /** @hidden */
    _minZ: number;
    /** @hidden */
    _maxZ: number;
    constructor(name: string, scene: Scene);
    /**
     * "GroundMesh"
     * @returns "GroundMesh"
     */
    getClassName(): string;
    /**
     * The minimum of x and y subdivisions
     */
    get subdivisions(): number;
    /**
     * X subdivisions
     */
    get subdivisionsX(): number;
    /**
     * Y subdivisions
     */
    get subdivisionsY(): number;
    /**
     * This function will update an octree to help to select the right submeshes for rendering, picking and collision computations.
     * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
     * @param chunksCount the number of subdivisions for x and y
     * @param octreeBlocksSize (Default: 32)
     */
    optimize(chunksCount: number, octreeBlocksSize?: number): void;
    /**
     * Returns a height (y) value in the Worl system :
     * the ground altitude at the coordinates (x, z) expressed in the World system.
     * @param x x coordinate
     * @param z z coordinate
     * @returns the ground y position if (x, z) are outside the ground surface.
     */
    getHeightAtCoordinates(x: number, z: number): number;
    /**
     * Returns a normalized vector (Vector3) orthogonal to the ground
     * at the ground coordinates (x, z) expressed in the World system.
     * @param x x coordinate
     * @param z z coordinate
     * @returns Vector3(0.0, 1.0, 0.0) if (x, z) are outside the ground surface.
     */
    getNormalAtCoordinates(x: number, z: number): Vector3;
    /**
     * Updates the Vector3 passed a reference with a normalized vector orthogonal to the ground
     * at the ground coordinates (x, z) expressed in the World system.
     * Doesn't uptade the reference Vector3 if (x, z) are outside the ground surface.
     * @param x x coordinate
     * @param z z coordinate
     * @param ref vector to store the result
     * @returns the GroundMesh.
     */
    getNormalAtCoordinatesToRef(x: number, z: number, ref: Vector3): GroundMesh;
    /**
    * Force the heights to be recomputed for getHeightAtCoordinates() or getNormalAtCoordinates()
    * if the ground has been updated.
    * This can be used in the render loop.
    * @returns the GroundMesh.
    */
    updateCoordinateHeights(): GroundMesh;
    private _getFacetAt;
    private _initHeightQuads;
    private _computeHeightQuads;
    /**
     * Serializes this ground mesh
     * @param serializationObject object to write serialization to
     */
    serialize(serializationObject: any): void;
    /**
     * Parses a serialized ground mesh
     * @param parsedMesh the serialized mesh
     * @param scene the scene to create the ground mesh in
     * @returns the created ground mesh
     */
    static Parse(parsedMesh: any, scene: Scene): GroundMesh;
}


/**
     * "GroundMesh"
     * @returns "GroundMesh"
     */
getClassName(): string;


/**
     * This function will update an octree to help to select the right submeshes for rendering, picking and collision computations.
     * Please note that you must have a decent number of submeshes to get performance improvements when using an octree
     * @param chunksCount the number of subdivisions for x and y
     * @param octreeBlocksSize (Default: 32)
     */
optimize(chunksCount: number, octreeBlocksSize?: number): void;


/**
     * Returns a height (y) value in the Worl system :
     * the ground altitude at the coordinates (x, z) expressed in the World system.
     * @param x x coordinate
     * @param z z coordinate
     * @returns the ground y position if (x, z) are outside the ground surface.
     */
getHeightAtCoordinates(x: number, z: number): number;


/**
     * Returns a normalized vector (Vector3) orthogonal to the ground
     * at the ground coordinates (x, z) expressed in the World system.
     * @param x x coordinate
     * @param z z coordinate
     * @returns Vector3(0.0, 1.0, 0.0) if (x, z) are outside the ground surface.
     */
getNormalAtCoordinates(x: number, z: number): Vector3;


/**
     * Updates the Vector3 passed a reference with a normalized vector orthogonal to the ground
     * at the ground coordinates (x, z) expressed in the World system.
     * Doesn't uptade the reference Vector3 if (x, z) are outside the ground surface.
     * @param x x coordinate
     * @param z z coordinate
     * @param ref vector to store the result
     * @returns the GroundMesh.
     */
getNormalAtCoordinatesToRef(x: number, z: number, ref: Vector3): GroundMesh;


/**
    * Force the heights to be recomputed for getHeightAtCoordinates() or getNormalAtCoordinates()
    * if the ground has been updated.
    * This can be used in the render loop.
    * @returns the GroundMesh.
    */
updateCoordinateHeights(): GroundMesh;


/**
     * Serializes this ground mesh
     * @param serializationObject object to write serialization to
     */
serialize(serializationObject: any): void;


/**
     * Parses a serialized ground mesh
     * @param parsedMesh the serialized mesh
     * @param scene the scene to create the ground mesh in
     * @returns the created ground mesh
     */
static Parse(parsedMesh: any, scene: Scene): GroundMesh;


({ children }) => {
  const history = useHistory()

  const isDisabled = history.length <= 2

  const handleClick = useCallback(() => history.goBack(), [history])

  return (
    <div
      className={classNames('Back', { disabled: isDisabled })}
      onClick={handleClick}
    >
      {children}
    </div>


classNames('Back', { disabled: isDisabled })


export type Track = {
  id: string;
  name: string;
  link: string;
  artist: string[] | null;
  album: string[] | null;
  lastPlayed?: string;
  length: string;
};


@Component({
  selector: 'app-resources',
  templateUrl: './resources.component.html',
  styleUrls: ['./resources.component.scss']
})
export class ResourcesComponent implements OnInit {
  constructor() {}

  ngOnInit() {
    // Specifies the language library to load for Highlights.js instead of loading all languages
    hljs.registerLanguage('javascript', javascript);
  }
}


ngOnInit() {
    // Specifies the language library to load for Highlights.js instead of loading all languages
    hljs.registerLanguage('javascript', javascript);
  }


export function loadDataSources(): ThunkResult<void> {
  return async (dispatch) => {
    const response = await getBackendSrv().get('/api/datasources');
    dispatch(dataSourcesLoaded(response));
  };
}


export function loadDataSource(uid: string): ThunkResult<void> {
  return async (dispatch) => {
    const dataSource = await getDataSourceUsingUidOrId(uid);
    const pluginInfo = (await getPluginSettings(dataSource.type)) as DataSourcePluginMeta;
    const plugin = await importDataSourcePlugin(pluginInfo);
    const isBackend = plugin.DataSourceClass.prototype instanceof DataSourceWithBackend;
    const meta = {
      ...pluginInfo,
      isBackend: isBackend,
    };
    dispatch(dataSourceLoaded(dataSource));
    dispatch(dataSourceMetaLoaded(meta));

    plugin.meta = meta;
    dispatch(updateNavIndex(buildNavModel(dataSource, plugin)));
  };
}


/**
 * Get data source by uid or id, if old id detected handles redirect
 */
async function getDataSourceUsingUidOrId(uid: string): Promise<DataSourceSettings> {
  // Try first with uid api
  try {
    const byUid = await getBackendSrv()
      .fetch<DataSourceSettings>({
        method: 'GET',
        url: `/api/datasources/uid/${uid}`,
        showErrorAlert: false,
      })
      .toPromise();

    if (byUid.ok) {
      return byUid.data;
    }
  } catch (err) {
    console.log('Failed to lookup data source by uid', err);
  }

  // try lookup by old db id
  const id = parseInt(uid, 10);
  if (!Number.isNaN(id)) {
    const response = await getBackendSrv()
      .fetch<DataSourceSettings>({
        method: 'GET',
        url: `/api/datasources/${id}`,
        showErrorAlert: false,
      })
      .toPromise();

    // Not ideal to do a full page reload here but so tricky to handle this
    // otherwise We can update the location using react router, but need to
    // fully reload the route as the nav model page index is not matching with
    // the url in that case. And react router has no way to unmount remount a
    // route
    if (response.ok && response.data.id.toString() === uid) {
      window.location.href = locationUtil.assureBaseUrl(`/datasources/edit/${response.data.uid}`);
      return {} as DataSourceSettings; // avoids flashing an error
    }
  }

  throw Error('Could not find data source');
}


export function addDataSource(plugin: DataSourcePluginMeta): ThunkResult<void> {
  return async (dispatch, getStore) => {
    await dispatch(loadDataSources());

    const dataSources = getStore().dataSources.dataSources;

    const newInstance = {
      name: plugin.name,
      type: plugin.id,
      access: 'proxy',
      isDefault: dataSources.length === 0,
    };

    if (nameExits(dataSources, newInstance.name)) {
      newInstance.name = findNewName(dataSources, newInstance.name);
    }

    const result = await getBackendSrv().post('/api/datasources', newInstance);
    locationService.push(`/datasources/edit/${result.datasource.uid}`);
  };
}


export function loadDataSourcePlugins(): ThunkResult<void> {
  return async (dispatch) => {
    dispatch(dataSourcePluginsLoad());
    const plugins = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' });
    const categories = buildCategories(plugins);
    dispatch(dataSourcePluginsLoaded({ plugins, categories }));
  };
}


export function updateDataSource(dataSource: DataSourceSettings): ThunkResult<void> {
  return async (dispatch) => {
    await getBackendSrv().put(`/api/datasources/${dataSource.id}`, dataSource); // by UID not yet supported
    await updateFrontendSettings();
    return dispatch(loadDataSource(dataSource.uid));
  };
}


export function deleteDataSource(): ThunkResult<void> {
  return async (dispatch, getStore) => {
    const dataSource = getStore().dataSources.dataSource;

    await getBackendSrv().delete(`/api/datasources/${dataSource.id}`);
    await updateFrontendSettings();

    locationService.push('/datasources');
  };
}


export function nameExits(dataSources: ItemWithName[], name: string) {
  return (
    dataSources.filter((dataSource) => {
      return dataSource.name.toLowerCase() === name.toLowerCase();
    }).length > 0
  );
}


export function findNewName(dataSources: ItemWithName[], name: string) {
  // Need to loop through current data sources to make sure
  // the name doesn't exist
  while (nameExits(dataSources, name)) {
    // If there's a duplicate name that doesn't end with '-x'
    // we can add -1 to the name and be done.
    if (!nameHasSuffix(name)) {
      name = `${name}-1`;
    } else {
      // if there's a duplicate name that ends with '-x'
      // we can try to increment the last digit until the name is unique

      // remove the 'x' part and replace it with the new number
      name = `${getNewName(name)}${incrementLastDigit(getLastDigit(name))}`;
    }
  }

  return name;
}


function updateFrontendSettings() {
  return getBackendSrv()
    .get('/api/frontend/settings')
    .then((settings: any) => {
      config.datasources = settings.datasources;
      config.defaultDatasource = settings.defaultDatasource;
      getDatasourceSrv().init(config.datasources, settings.defaultDatasource);
    });
}


function nameHasSuffix(name: string) {
  return name.endsWith('-', name.length - 1);
}


function getLastDigit(name: string) {
  return parseInt(name.slice(-1), 10);
}


function incrementLastDigit(digit: number) {
  return isNaN(digit) ? 1 : digit + 1;
}


function getNewName(name: string) {
  return name.slice(0, name.length - 1);
}


(
  pageId: string,
  dependencies: InitDataSourceSettingDependencies = {
    loadDataSource,
    getDataSource,
    getDataSourceMeta,
    importDataSourcePlugin,
  }
): ThunkResult<void> => {
  return async (dispatch, getState) => {
    if (!pageId) {
      dispatch(initDataSourceSettingsFailed(new Error('Invalid ID')));
      return;
    }

    try {
      await dispatch(dependencies.loadDataSource(pageId));

      // have we already loaded the plugin then we can skip the steps below?
      if (getState().dataSourceSettings.plugin) {
        return;
      }

      const dataSource = dependencies.getDataSource(getState().dataSources, pageId);
      const dataSourceMeta = dependencies.getDataSourceMeta(getState().dataSources, dataSource!.type);
      const importedPlugin = await dependencies.importDataSourcePlugin(dataSourceMeta);

      dispatch(initDataSourceSettingsSucceeded(importedPlugin));
    } catch (err) {
      console.error('Failed to import plugin module', err);
      dispatch(initDataSourceSettingsFailed(err));
    }
  };
}


async (dispatch, getState) => {
    if (!pageId) {
      dispatch(initDataSourceSettingsFailed(new Error('Invalid ID')));
      return;
    }

    try {
      await dispatch(dependencies.loadDataSource(pageId));

      // have we already loaded the plugin then we can skip the steps below?
      if (getState().dataSourceSettings.plugin) {
        return;
      }

      const dataSource = dependencies.getDataSource(getState().dataSources, pageId);
      const dataSourceMeta = dependencies.getDataSourceMeta(getState().dataSources, dataSource!.type);
      const importedPlugin = await dependencies.importDataSourcePlugin(dataSourceMeta);

      dispatch(initDataSourceSettingsSucceeded(importedPlugin));
    } catch (err) {
      console.error('Failed to import plugin module', err);
      dispatch(initDataSourceSettingsFailed(err));
    }
  }


(
  dataSourceName: string,
  dependencies: TestDataSourceDependencies = {
    getDatasourceSrv,
    getBackendSrv,
  }
): ThunkResult<void> => {
  return async (dispatch: ThunkDispatch, getState) => {
    const dsApi = await dependencies.getDatasourceSrv().get(dataSourceName);

    if (!dsApi.testDatasource) {
      return;
    }

    dispatch(testDataSourceStarting());

    dependencies.getBackendSrv().withNoBackendCache(async () => {
      try {
        const result = await dsApi.testDatasource();

        dispatch(testDataSourceSucceeded(result));
      } catch (err) {
        const { statusText, message: errMessage, details, data } = err;

        const message = errMessage || data?.message || 'HTTP error ' + statusText;

        dispatch(testDataSourceFailed({ message, details }));
      }
    });
  };
}


async (dispatch: ThunkDispatch, getState) => {
    const dsApi = await dependencies.getDatasourceSrv().get(dataSourceName);

    if (!dsApi.testDatasource) {
      return;
    }

    dispatch(testDataSourceStarting());

    dependencies.getBackendSrv().withNoBackendCache(async () => {
      try {
        const result = await dsApi.testDatasource();

        dispatch(testDataSourceSucceeded(result));
      } catch (err) {
        const { statusText, message: errMessage, details, data } = err;

        const message = errMessage || data?.message || 'HTTP error ' + statusText;

        dispatch(testDataSourceFailed({ message, details }));
      }
    });
  }


async () => {
      try {
        const result = await dsApi.testDatasource();

        dispatch(testDataSourceSucceeded(result));
      } catch (err) {
        const { statusText, message: errMessage, details, data } = err;

        const message = errMessage || data?.message || 'HTTP error ' + statusText;

        dispatch(testDataSourceFailed({ message, details }));
      }
    }


async (dispatch) => {
    const response = await getBackendSrv().get('/api/datasources');
    dispatch(dataSourcesLoaded(response));
  }


async (dispatch) => {
    const dataSource = await getDataSourceUsingUidOrId(uid);
    const pluginInfo = (await getPluginSettings(dataSource.type)) as DataSourcePluginMeta;
    const plugin = await importDataSourcePlugin(pluginInfo);
    const isBackend = plugin.DataSourceClass.prototype instanceof DataSourceWithBackend;
    const meta = {
      ...pluginInfo,
      isBackend: isBackend,
    };
    dispatch(dataSourceLoaded(dataSource));
    dispatch(dataSourceMetaLoaded(meta));

    plugin.meta = meta;
    dispatch(updateNavIndex(buildNavModel(dataSource, plugin)));
  }


async (dispatch, getStore) => {
    await dispatch(loadDataSources());

    const dataSources = getStore().dataSources.dataSources;

    const newInstance = {
      name: plugin.name,
      type: plugin.id,
      access: 'proxy',
      isDefault: dataSources.length === 0,
    };

    if (nameExits(dataSources, newInstance.name)) {
      newInstance.name = findNewName(dataSources, newInstance.name);
    }

    const result = await getBackendSrv().post('/api/datasources', newInstance);
    locationService.push(`/datasources/edit/${result.datasource.uid}`);
  }


async (dispatch) => {
    dispatch(dataSourcePluginsLoad());
    const plugins = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' });
    const categories = buildCategories(plugins);
    dispatch(dataSourcePluginsLoaded({ plugins, categories }));
  }


async (dispatch) => {
    await getBackendSrv().put(`/api/datasources/${dataSource.id}`, dataSource); // by UID not yet supported
    await updateFrontendSettings();
    return dispatch(loadDataSource(dataSource.uid));
  }


async (dispatch, getStore) => {
    const dataSource = getStore().dataSources.dataSource;

    await getBackendSrv().delete(`/api/datasources/${dataSource.id}`);
    await updateFrontendSettings();

    locationService.push('/datasources');
  }


(dataSource) => {
      return dataSource.name.toLowerCase() === name.toLowerCase();
    }


(settings: any) => {
      config.datasources = settings.datasources;
      config.defaultDatasource = settings.defaultDatasource;
      getDatasourceSrv().init(config.datasources, settings.defaultDatasource);
    }


export interface DataSourceTypesLoadedPayload {
  plugins: DataSourcePluginMeta[];
  categories: DataSourcePluginCategory[];
}


export interface InitDataSourceSettingDependencies {
  loadDataSource: typeof loadDataSource;
  getDataSource: typeof getDataSource;
  getDataSourceMeta: typeof getDataSourceMeta;
  importDataSourcePlugin: typeof importDataSourcePlugin;
}


export interface TestDataSourceDependencies {
  getDatasourceSrv: typeof getDataSourceSrv;
  getBackendSrv: typeof getBackendSrv;
}


interface ItemWithName {
  name: string;
}


() => {
  const dimensionX = 5;
  const dimensionY = 10;
  const atomCount = 3;

  const grid = buildGameGrid(dimensionX, dimensionY, atomCount);

  it('should create expected count of cells', () => {
    expect([...grid.values()].length).toEqual(dimensionX * dimensionY);
  });

  it('should create expected count of atoms', () => {
    const actualAtomCount = [...grid.values()].filter(
      (cell) => cell.hasAtom,
    ).length;

    expect(actualAtomCount).toEqual(atomCount);
  });

  it('should report expected min/max X,Y bounds', () => {
    expect(grid.minX).toEqual(1);
    expect(grid.maxX).toEqual(dimensionX);
    expect(grid.minY).toEqual(1);
    expect(grid.maxY).toEqual(dimensionY);
  });
}


() => {
    expect([...grid.values()].length).toEqual(dimensionX * dimensionY);
  }


() => {
    const actualAtomCount = [...grid.values()].filter(
      (cell) => cell.hasAtom,
    ).length;

    expect(actualAtomCount).toEqual(atomCount);
  }


(cell) => cell.hasAtom


() => {
    expect(grid.minX).toEqual(1);
    expect(grid.maxX).toEqual(dimensionX);
    expect(grid.minY).toEqual(1);
    expect(grid.maxY).toEqual(dimensionY);
  }


/**MotorON select motor channel and direction. The speed motor is adjustable between 0 to 100.
      * @param Speed  	percent of maximum Speed, eg: 50
      */
//% blockId="ibit_MotorON" block="motor %motorSEL | direction %motorDIR | speed %Speed"
//% Speed.min=0 Speed.max=100
//% weight=100
export function MotorON(Channel:motorSEL, Direction:motorDIR, Speed:number): void {
        let motorspeed = pins.map(Speed, 0, 100, 0, 1023)

        if (Channel == motorSEL.M1 && Direction == motorDIR.Forward) {
           pins.analogWritePin(AnalogPin.P13, motorspeed)
           pins.digitalWritePin(DigitalPin.P14, 0)
        }
        else if (Channel == motorSEL.M2 && Direction == motorDIR.Forward) {
           pins.analogWritePin(AnalogPin.P15, motorspeed)
           pins.digitalWritePin(DigitalPin.P16, 0)
        }
        else if (Channel == motorSEL.M1 && Direction == motorDIR.Reverse) {
           pins.analogWritePin(AnalogPin.P14, motorspeed)
           pins.digitalWritePin(DigitalPin.P13, 0)
        }
        else if (Channel == motorSEL.M2 && Direction == motorDIR.Reverse) {
           pins.analogWritePin(AnalogPin.P16, motorspeed)
           pins.digitalWritePin(DigitalPin.P15, 0)
        }
        else if (Channel == motorSEL.M12 && Direction == motorDIR.Forward) {
            pins.analogWritePin(AnalogPin.P13, motorspeed)
            pins.digitalWritePin(DigitalPin.P14, 0)
	    pins.analogWritePin(AnalogPin.P15, motorspeed)
            pins.digitalWritePin(DigitalPin.P16, 0)
        }
        else if (Channel == motorSEL.M12 && Direction == motorDIR.Reverse) {
            pins.analogWritePin(AnalogPin.P14, motorspeed)
            pins.digitalWritePin(DigitalPin.P13, 0)
            pins.analogWritePin(AnalogPin.P16, motorspeed)
            pins.digitalWritePin(DigitalPin.P15, 0)
        }
    }


/**MotorAB set motor AB and direction. The speed motor seperate and adjustable between 0 to 100.
      * @param speedA percent of maximum Speed, eg: 50
      * @param speedB percent of maximum Speed, eg: 50
      */
//% blockId="ibit_MotorAB" block="motor[AB] direction %motorDIR |speed[A] %speedA |speed[B] %speedB"
//% speedA.min=0 speedA.max=100
//% speedB.min=0 speedB.max=100
//% weight=100
export function MotorAB(Direction:motorDIR, speedA:number, speedB:number): void {
        let motorspeedA = pins.map(speedA, 0, 100, 0, 1023)
        let motorspeedB = pins.map(speedB, 0, 100, 0, 1023)

        if (Direction == motorDIR.Forward) {
            pins.analogWritePin(AnalogPin.P13, motorspeedA)
            pins.digitalWritePin(DigitalPin.P14, 0)
	    pins.analogWritePin(AnalogPin.P15, motorspeedB)
            pins.digitalWritePin(DigitalPin.P16, 0)
        }
        if (Direction == motorDIR.Reverse) {
            pins.analogWritePin(AnalogPin.P14, motorspeedA)
            pins.digitalWritePin(DigitalPin.P13, 0)
            pins.analogWritePin(AnalogPin.P16, motorspeedB)
            pins.digitalWritePin(DigitalPin.P15, 0)
        }
    }


/**
     * Turns off the motor
     * @param motor which motor to turn off
     */
//% blockId="Motor_motoroff" block="motor %motorSEL | stop mode %StopMode"
export function motorOFF(Channel:motorSEL, stop:StopMode): void {
        if (Channel == motorSEL.M12 && stop == StopMode.Brake) {
		pins.digitalWritePin(DigitalPin.P13, 1)
		pins.digitalWritePin(DigitalPin.P14, 1)
		pins.digitalWritePin(DigitalPin.P15, 1)
		pins.digitalWritePin(DigitalPin.P16, 1)
        }
        else if (Channel == motorSEL.M12 && stop == StopMode.Coast) {
		pins.digitalWritePin(DigitalPin.P13, 0)
		pins.digitalWritePin(DigitalPin.P14, 0)
		pins.digitalWritePin(DigitalPin.P15, 0)
		pins.digitalWritePin(DigitalPin.P16, 0)
        }
        else if (Channel == motorSEL.M1 && stop == StopMode.Brake) {
		pins.digitalWritePin(DigitalPin.P13, 1)
		pins.digitalWritePin(DigitalPin.P14, 1)
        }
        else if (Channel == motorSEL.M1 && stop == StopMode.Coast) {
		pins.digitalWritePin(DigitalPin.P13, 0)
		pins.digitalWritePin(DigitalPin.P14, 0)
        }
        else if (Channel == motorSEL.M2 && stop == StopMode.Brake) {
		pins.digitalWritePin(DigitalPin.P15, 1)
		pins.digitalWritePin(DigitalPin.P16, 1)
        }
        else if (Channel == motorSEL.M2 && stop == StopMode.Coast) {
 		pins.digitalWritePin(DigitalPin.P15, 0)
		pins.digitalWritePin(DigitalPin.P16, 0)
        }
    }


/**
 * Turn direction with dual motors for line follow robot.
 * @param turnDIR      turn Left or Right
 * @param speedturn    motor speed; eg: 40
*/
//% blockId="Motor_followlineTurn" block="turn %Turn | speed %speedturn"
//% speedturn.min=0 speedturn.max=100
export function followlineTurn(turnDIR:Turn, speedturn:number): void {
      let motorspeedturn = pins.map(speedturn,0,100,0,1023)
      if (turnDIR == Turn.Left) {
 	    pins.digitalWritePin(DigitalPin.P13, 0)
	    pins.digitalWritePin(DigitalPin.P14, 0)
	    pins.analogWritePin(AnalogPin.P15, motorspeedturn)
            pins.digitalWritePin(DigitalPin.P16, 0)
       }
      if (turnDIR == Turn.Right) {
            pins.analogWritePin(AnalogPin.P14, motorspeedturn)
            pins.digitalWritePin(DigitalPin.P13, 0)
 	    pins.digitalWritePin(DigitalPin.P15, 0)
	    pins.digitalWritePin(DigitalPin.P16, 0)
       }
    }


/**
 * Execute dual motor to rotate with delay mS time to brake mode.
 * @param rotateDIR         	rotate robot direction.
 * @param speedrotate   	speed of motor; eg: 50
 * @param pausems       	time to brake; eg: 400
 */
//% blockId="Motor_rotate" block="rotate  %Turn | speed %speedrotate | pause %pausems |mS"
//% speedrotate.min=0 speedrotate.max=100
export function Rotate(rotateDIR:Turn, speedrotate:number, pausems: number): void {
      let motorspeedrotate = pins.map(speedrotate,0,100,0,1023)
      if (rotateDIR == Turn.Left) {
           pins.analogWritePin(AnalogPin.P14, motorspeedrotate)
           pins.digitalWritePin(DigitalPin.P13, 0)
           pins.analogWritePin(AnalogPin.P15, motorspeedrotate)
           pins.digitalWritePin(DigitalPin.P16, 0)
	   basic.pause(pausems)
	   pins.digitalWritePin(DigitalPin.P13, 1)
	   pins.digitalWritePin(DigitalPin.P14, 1)
	   pins.digitalWritePin(DigitalPin.P15, 1)
	   pins.digitalWritePin(DigitalPin.P16, 1)
      }
      if (rotateDIR == Turn.Right) {
           pins.analogWritePin(AnalogPin.P13, motorspeedrotate)
           pins.digitalWritePin(DigitalPin.P14, 0)
           pins.analogWritePin(AnalogPin.P16, motorspeedrotate)
           pins.digitalWritePin(DigitalPin.P15, 0)
	   basic.pause(pausems)
	   pins.digitalWritePin(DigitalPin.P13, 1)
	   pins.digitalWritePin(DigitalPin.P14, 1)
	   pins.digitalWritePin(DigitalPin.P15, 1)
	   pins.digitalWritePin(DigitalPin.P16, 1)
       }
    }


/**
 * Execute dual motor to rotate Left and Right non stop for use linefollow mode.
 * @param rotateLINE     	rotate robot direction.
 * @param speedline     	motor speed; eg: 50
 */
//% blockId="Motor_rotatenotime"  block="rotate %Turn |speed %speedline"
//% speedline.min=0 speedline.max=100
export function RotateNOTIME(rotateLINE:Turn, speedline:number): void {
      let motorspeedline = pins.map(speedline,0,100,0,1023)
      if (rotateLINE == Turn.Left) {
           pins.analogWritePin(AnalogPin.P14, motorspeedline)
           pins.digitalWritePin(DigitalPin.P13, 0)
           pins.analogWritePin(AnalogPin.P15, motorspeedline)
           pins.digitalWritePin(DigitalPin.P16, 0)
      }
      if (rotateLINE == Turn.Right) {
           pins.analogWritePin(AnalogPin.P13, motorspeedline)
           pins.digitalWritePin(DigitalPin.P14, 0)
           pins.analogWritePin(AnalogPin.P16, motorspeedline)
           pins.digitalWritePin(DigitalPin.P15, 0)
       }
    }


/**
 * Execute puase time
 * @param pausetime  	mSec to delay; eg: 100
*/
//% pausetime.min=1  pausetime.max=100000
//% blockId=Motor_TimePAUSE block="pause | %pausetime | mS"
export function TimePAUSE(pausetime: number): void {
	basic.pause(pausetime)
        }


/**
  * Coding for control of Motor.
  */
enum motorSEL {
    //% block="A"
    M1,
    //% block="B"
    M2,
     //% block="AB"
    M12
}


enum motorDIR {
    //% block="Forward"
    Forward,
    //% block="Reverse"
    Reverse
}


enum StopMode {
        //% block="brake"
        Brake,
        //% block="coast"
        Coast
}


enum Turn {
        //% block="left"
        Left,
        //% block="right"
        Right
    }


export class I18nKeyEvaluationResult {
  public key: string;
  public value: string = (void 0)!;
  public attributes: string[];

  public constructor(keyExpr: string) {
    const re = /\[([a-z\-, ]*)\]/ig;
    this.attributes = [];

    // check if a attribute was specified in the key
    const matches = re.exec(keyExpr);
    if (matches) {
      keyExpr = keyExpr.replace(matches[0], '');
      this.attributes = matches[1].split(',');
    }
    this.key = keyExpr;
  }
}


/**
 * Translation service class.
 */
export class I18nService implements I18N {

  public i18next: i18nextCore.i18n;
  /**
   * This is used for i18next initialization and awaited for before the bind phase.
   * If need be (usually there is none), this task can be awaited for explicitly in client code.
   */
  public readonly task: ILifecycleTask;
  private options!: I18nInitOptions;
  private readonly intl: typeof Intl;

  public constructor(@I18nWrapper i18nextWrapper: I18nextWrapper, @I18nInitOptions options: I18nInitOptions, @IEventAggregator private readonly ea: IEventAggregator, @ISignaler private readonly signaler: ISignaler) {
    this.i18next = i18nextWrapper.i18next;
    this.task = new PromiseTask(this.initializeI18next(options), null, this);
    this.intl = PLATFORM.global.Intl;
  }

  public evaluate(keyExpr: string, options?: i18nextCore.TOptions): I18nKeyEvaluationResult[] {
    const parts = keyExpr.split(';');
    const results: I18nKeyEvaluationResult[] = [];
    for (const part of parts) {
      const result = new I18nKeyEvaluationResult(part);
      const key = result.key;
      const translation = this.tr(key, options);
      if (this.options.skipTranslationOnMissingKey && translation === key) {
        // TODO change this once the logging infra is there.
        console.warn(`Couldn't find translation for key: ${key}`);
      } else {
        result.value = translation;
        results.push(result);
      }
    }
    return results;
  }

  public tr(key: string | string[], options?: i18nextCore.TOptions): string {
    return this.i18next.t(key, options);
  }

  public getLocale(): string {
    return this.i18next.language;
  }
  public async setLocale(newLocale: string): Promise<void> {
    const oldLocale = this.getLocale();
    await this.i18next.changeLanguage(newLocale);
    this.ea.publish(Signals.I18N_EA_CHANNEL, { oldLocale, newLocale });
    this.signaler.dispatchSignal(Signals.I18N_SIGNAL);
  }

  public createNumberFormat(options?: Intl.NumberFormatOptions, locales?: string | string[]): Intl.NumberFormat {
    return this.intl.NumberFormat(locales || this.getLocale(), options);
  }

  public nf(input: number, options?: Intl.NumberFormatOptions, locales?: string | string[]): string {
    return this.createNumberFormat(options, locales).format(input);
  }

  public createDateTimeFormat(options?: Intl.DateTimeFormatOptions, locales?: string | string[]): Intl.DateTimeFormat {
    return this.intl.DateTimeFormat(locales || this.getLocale(), options);
  }

  public df(input: number | Date, options?: Intl.DateTimeFormatOptions, locales?: string | string[]): string {
    return this.createDateTimeFormat(options, locales).format(input);
  }

  public uf(numberLike: string, locale?: string): number {
    // Unfortunately the Intl specs does not specify a way to get the thousand and decimal separators for a given locale.
    // Only straightforward way would be to include the CLDR data and query for the separators, which certainly is a overkill.
    const comparer = this.nf(10000 / 3, undefined, locale);

    let thousandSeparator = comparer[1];
    const decimalSeparator = comparer[5];

    if (thousandSeparator === '.') {
      thousandSeparator = '\\.';
    }

    // remove all thousand separators
    const result = numberLike.replace(new RegExp(thousandSeparator, 'g'), '')
      // remove non-numeric signs except -> , .
      .replace(/[^\d.,-]/g, '')
      // replace original decimalSeparator with english one
      .replace(decimalSeparator, '.');

    // return real number
    return Number(result);
  }

  public createRelativeTimeFormat(options?: Intl.RelativeTimeFormatOptions, locales?: string | string[]): Intl.RelativeTimeFormat {
    return new this.intl.RelativeTimeFormat(locales || this.getLocale(), options);
  }

  public rt(input: Date, options?: Intl.RelativeTimeFormatOptions, locales?: string | string[]): string {
    let difference = input.getTime() - this.now();
    const epsilon = this.options.rtEpsilon! * (difference > 0 ? 1 : 0);

    const formatter = this.createRelativeTimeFormat(options, locales);

    let value: number = difference / TimeSpan.Year;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'year');
    }

    value = difference / TimeSpan.Month;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'month');
    }

    value = difference / TimeSpan.Week;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'week');
    }

    value = difference / TimeSpan.Day;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'day');
    }

    value = difference / TimeSpan.Hour;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'hour');
    }

    value = difference / TimeSpan.Minute;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'minute');
    }

    difference = Math.abs(difference) < TimeSpan.Second ? TimeSpan.Second : difference;
    value = difference / TimeSpan.Second;
    return formatter.format(Math.round(value), 'second');
  }

  private now() {
    return new Date().getTime();
  }

  private async initializeI18next(options: I18nInitOptions) {
    const defaultOptions: I18nInitOptions = {
      lng: 'en',
      fallbackLng: ['en'],
      debug: false,
      plugins: [],
      rtEpsilon: 0.01,
      skipTranslationOnMissingKey: false,
    };
    this.options = { ...defaultOptions, ...options };
    for (const plugin of this.options.plugins!) {
      this.i18next.use(plugin);
    }
    await this.i18next.init(this.options);
  }
}


export interface I18N {
  i18next: i18nextCore.i18n;
  readonly task: ILifecycleTask;
  /**
   * Evaluates the `keyExpr` to translated values.
   * For a single key, `I18nService#tr` method can also be easily used.
   *
   * @example
   *  evaluate('key1;[attr]key2;[attr1,attr2]key3', [options]) => [
   *    {key: 'key1', attributes:[], value: 'translated_value_of_key1'}
   *    {key: 'key2', attributes:['attr'], value: 'translated_value_of_key2'}
   *    {key: 'key3', attributes:['attr1', 'attr2'], value: 'translated_value_of_key3'}
   *  ]
   */
  evaluate(keyExpr: string, options?: i18nextCore.TOptions): I18nKeyEvaluationResult[];
  tr(key: string | string[], options?: i18nextCore.TOptions): string;
  getLocale(): string;
  setLocale(newLocale: string): Promise<void>;
  /**
   * Returns `Intl.NumberFormat` instance with given `[options]`, and `[locales]` which can be used to format a number.
   * If the `locales` is skipped, then the `Intl.NumberFormat` instance is created using the currently active locale.
   */
  createNumberFormat(options?: Intl.NumberFormatOptions, locales?: string | string[]): Intl.NumberFormat;
  /**
   * Formats the given `input` number according to the given `[options]`, and `[locales]`.
   * If the `locales` is skipped, then the number is formatted using the currently active locale.
   *
   * @returns Formatted number.
   */
  nf(input: number, options?: Intl.NumberFormatOptions, locales?: string | string[]): string;
  /**
   * Unformats a given numeric string to a number.
   */
  uf(numberLike: string, locale?: string): number;
  /**
   * Returns `Intl.DateTimeFormat` instance with given `[options]`, and `[locales]` which can be used to format a date.
   * If the `locales` is skipped, then the `Intl.DateTimeFormat` instance is created using the currently active locale.
   */
  createDateTimeFormat(options?: Intl.DateTimeFormatOptions, locales?: string | string[]): Intl.DateTimeFormat;
  /**
   * Formats the given `input` date according to the given `[options]` and `[locales]`.
   * If the `locales` is skipped, then the date is formatted using the currently active locale.
   *
   * @returns Formatted date.
   */
  df(input: number | Date, options?: Intl.DateTimeFormatOptions, locales?: string | string[]): string;
  /**
   * Returns `Intl.RelativeTimeFormat` instance with given `[options]`, and `[locales]` which can be used to format a value with associated time unit.
   * If the `locales` is skipped, then the `Intl.RelativeTimeFormat` instance is created using the currently active locale.
   */
  createRelativeTimeFormat(options?: Intl.RelativeTimeFormatOptions, locales?: string | string[]): Intl.RelativeTimeFormat;
  /**
   * Returns a relative time format of the given `input` date as per the given `[options]`, and `[locales]`.
   * If the `locales` is skipped, then the currently active locale is used for formatting.
   */
  rt(input: Date, options?: Intl.RelativeTimeFormatOptions, locales?: string | string[]): string;
}


const enum TimeSpan {
  Second = 1000,
  Minute = Second * 60,
  Hour = Minute * 60,
  Day = Hour * 24,
  Week = Day * 7,
  Month = Day * 30,
  Year = Day * 365
}


public evaluate(keyExpr: string, options?: i18nextCore.TOptions): I18nKeyEvaluationResult[] {
    const parts = keyExpr.split(';');
    const results: I18nKeyEvaluationResult[] = [];
    for (const part of parts) {
      const result = new I18nKeyEvaluationResult(part);
      const key = result.key;
      const translation = this.tr(key, options);
      if (this.options.skipTranslationOnMissingKey && translation === key) {
        // TODO change this once the logging infra is there.
        console.warn(`Couldn't find translation for key: ${key}`);
      } else {
        result.value = translation;
        results.push(result);
      }
    }
    return results;
  }


public tr(key: string | string[], options?: i18nextCore.TOptions): string {
    return this.i18next.t(key, options);
  }


public getLocale(): string {
    return this.i18next.language;
  }


public async setLocale(newLocale: string): Promise<void> {
    const oldLocale = this.getLocale();
    await this.i18next.changeLanguage(newLocale);
    this.ea.publish(Signals.I18N_EA_CHANNEL, { oldLocale, newLocale });
    this.signaler.dispatchSignal(Signals.I18N_SIGNAL);
  }


public createNumberFormat(options?: Intl.NumberFormatOptions, locales?: string | string[]): Intl.NumberFormat {
    return this.intl.NumberFormat(locales || this.getLocale(), options);
  }


public nf(input: number, options?: Intl.NumberFormatOptions, locales?: string | string[]): string {
    return this.createNumberFormat(options, locales).format(input);
  }


public createDateTimeFormat(options?: Intl.DateTimeFormatOptions, locales?: string | string[]): Intl.DateTimeFormat {
    return this.intl.DateTimeFormat(locales || this.getLocale(), options);
  }


public df(input: number | Date, options?: Intl.DateTimeFormatOptions, locales?: string | string[]): string {
    return this.createDateTimeFormat(options, locales).format(input);
  }


public uf(numberLike: string, locale?: string): number {
    // Unfortunately the Intl specs does not specify a way to get the thousand and decimal separators for a given locale.
    // Only straightforward way would be to include the CLDR data and query for the separators, which certainly is a overkill.
    const comparer = this.nf(10000 / 3, undefined, locale);

    let thousandSeparator = comparer[1];
    const decimalSeparator = comparer[5];

    if (thousandSeparator === '.') {
      thousandSeparator = '\\.';
    }

    // remove all thousand separators
    const result = numberLike.replace(new RegExp(thousandSeparator, 'g'), '')
      // remove non-numeric signs except -> , .
      .replace(/[^\d.,-]/g, '')
      // replace original decimalSeparator with english one
      .replace(decimalSeparator, '.');

    // return real number
    return Number(result);
  }


public createRelativeTimeFormat(options?: Intl.RelativeTimeFormatOptions, locales?: string | string[]): Intl.RelativeTimeFormat {
    return new this.intl.RelativeTimeFormat(locales || this.getLocale(), options);
  }


public rt(input: Date, options?: Intl.RelativeTimeFormatOptions, locales?: string | string[]): string {
    let difference = input.getTime() - this.now();
    const epsilon = this.options.rtEpsilon! * (difference > 0 ? 1 : 0);

    const formatter = this.createRelativeTimeFormat(options, locales);

    let value: number = difference / TimeSpan.Year;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'year');
    }

    value = difference / TimeSpan.Month;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'month');
    }

    value = difference / TimeSpan.Week;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'week');
    }

    value = difference / TimeSpan.Day;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'day');
    }

    value = difference / TimeSpan.Hour;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'hour');
    }

    value = difference / TimeSpan.Minute;
    if (Math.abs(value + epsilon) >= 1) {
      return formatter.format(Math.round(value), 'minute');
    }

    difference = Math.abs(difference) < TimeSpan.Second ? TimeSpan.Second : difference;
    value = difference / TimeSpan.Second;
    return formatter.format(Math.round(value), 'second');
  }


private now() {
    return new Date().getTime();
  }


private async initializeI18next(options: I18nInitOptions) {
    const defaultOptions: I18nInitOptions = {
      lng: 'en',
      fallbackLng: ['en'],
      debug: false,
      plugins: [],
      rtEpsilon: 0.01,
      skipTranslationOnMissingKey: false,
    };
    this.options = { ...defaultOptions, ...options };
    for (const plugin of this.options.plugins!) {
      this.i18next.use(plugin);
    }
    await this.i18next.init(this.options);
  }


export default class DeviceInitCmd extends Command {
	public static description = stripIndent`
		Initialise a device with balenaOS.

		Initialise a device by downloading the OS image of a certain application
		and writing it to an SD Card.

		Note, if the application option is omitted it will be prompted
		for interactively.
		`;
	public static examples = [
		'$ balena device init',
		'$ balena device init --application MyApp',
	];

	public static usage = 'device init';

	public static flags: flags.Input<FlagsDef> = {
		application: cf.application,
		app: cf.app,
		yes: cf.yes,
		advanced: flags.boolean({
			char: 'v',
			description: 'show advanced configuration options',
		}),
		'os-version': flags.string({
			description: stripIndent`
				exact version number, or a valid semver range,
				or 'latest' (includes pre-releases),
				or 'default' (excludes pre-releases if at least one stable version is available),
				or 'recommended' (excludes pre-releases, will fail if only pre-release versions are available),
				or 'menu' (will show the interactive menu)
				`,
		}),
		drive: cf.drive,
		config: flags.string({
			description: 'path to the config JSON file, see `balena os build-config`',
		}),
		help: cf.help,
	};

	public static authenticated = true;

	public async run() {
		const { flags: options } = this.parse<FlagsDef, {}>(DeviceInitCmd);

		// Imports
		const { promisify } = await import('util');
		const rimraf = promisify(await import('rimraf'));
		const tmp = await import('tmp');
		const tmpNameAsync = promisify(tmp.tmpName);
		tmp.setGracefulCleanup();
		const balena = getBalenaSdk();
		const { downloadOSImage } = await import('../../utils/cloud');
		const Logger = await import('../../utils/logger');

		const logger = Logger.getLogger();

		// Consolidate application options
		options.application = options.application || options.app;
		delete options.app;

		// Get application and
		const application = (await balena.models.application.get(
			options['application'] ||
				(await (await import('../../utils/patterns')).selectApplication()),
			{
				$expand: {
					is_for__device_type: {
						$select: 'slug',
					},
				},
			},
		)) as ApplicationWithDeviceType;

		// Register new device
		const deviceUuid = balena.models.device.generateUniqueKey();
		console.info(`Registering to ${application.app_name}: ${deviceUuid}`);
		await balena.models.device.register(application.id, deviceUuid);
		const device = await balena.models.device.get(deviceUuid);

		// Download OS, configure, and flash
		const tmpPath = (await tmpNameAsync()) as string;
		try {
			logger.logDebug(`Downloading OS image...`);
			const osVersion = options['os-version'] || 'default';
			const deviceType = application.is_for__device_type[0].slug;
			await downloadOSImage(deviceType, tmpPath, osVersion);

			logger.logDebug(`Configuring OS image...`);
			await this.configureOsImage(tmpPath, device.uuid, options);

			logger.logDebug(`Writing OS image...`);
			await this.writeOsImage(tmpPath, deviceType, options);
		} catch (e) {
			// Remove device in failed cases
			try {
				logger.logDebug(`Process failed, removing device ${device.uuid}`);
				await balena.models.device.remove(device.uuid);
			} catch (e) {
				// Ignore removal failures, and throw original error
			}
			throw e;
		} finally {
			// Remove temp download
			logger.logDebug(`Removing temporary OS image download...`);
			await rimraf(tmpPath);
		}

		console.log('Done');
		return device.uuid;
	}

	async configureOsImage(path: string, uuid: string, options: FlagsDef) {
		const configureCommand = ['os', 'configure', path, '--device', uuid];
		if (options.config) {
			configureCommand.push('--config', options.config);
		} else if (options.advanced) {
			configureCommand.push('--advanced');
		}
		await runCommand(configureCommand);
	}

	async writeOsImage(path: string, deviceType: string, options: FlagsDef) {
		const osInitCommand = ['os', 'initialize', path, '--type', deviceType];
		if (options.yes) {
			osInitCommand.push('--yes');
		}
		if (options.drive) {
			osInitCommand.push('--drive', options.drive);
		}
		await runCommand(osInitCommand);
	}
}


interface FlagsDef {
	application?: string;
	app?: string;
	yes: boolean;
	advanced: boolean;
	'os-version'?: string;
	drive?: string;
	config?: string;
	help: void;
}


public async run() {
		const { flags: options } = this.parse<FlagsDef, {}>(DeviceInitCmd);

		// Imports
		const { promisify } = await import('util');
		const rimraf = promisify(await import('rimraf'));
		const tmp = await import('tmp');
		const tmpNameAsync = promisify(tmp.tmpName);
		tmp.setGracefulCleanup();
		const balena = getBalenaSdk();
		const { downloadOSImage } = await import('../../utils/cloud');
		const Logger = await import('../../utils/logger');

		const logger = Logger.getLogger();

		// Consolidate application options
		options.application = options.application || options.app;
		delete options.app;

		// Get application and
		const application = (await balena.models.application.get(
			options['application'] ||
				(await (await import('../../utils/patterns')).selectApplication()),
			{
				$expand: {
					is_for__device_type: {
						$select: 'slug',
					},
				},
			},
		)) as ApplicationWithDeviceType;

		// Register new device
		const deviceUuid = balena.models.device.generateUniqueKey();
		console.info(`Registering to ${application.app_name}: ${deviceUuid}`);
		await balena.models.device.register(application.id, deviceUuid);
		const device = await balena.models.device.get(deviceUuid);

		// Download OS, configure, and flash
		const tmpPath = (await tmpNameAsync()) as string;
		try {
			logger.logDebug(`Downloading OS image...`);
			const osVersion = options['os-version'] || 'default';
			const deviceType = application.is_for__device_type[0].slug;
			await downloadOSImage(deviceType, tmpPath, osVersion);

			logger.logDebug(`Configuring OS image...`);
			await this.configureOsImage(tmpPath, device.uuid, options);

			logger.logDebug(`Writing OS image...`);
			await this.writeOsImage(tmpPath, deviceType, options);
		} catch (e) {
			// Remove device in failed cases
			try {
				logger.logDebug(`Process failed, removing device ${device.uuid}`);
				await balena.models.device.remove(device.uuid);
			} catch (e) {
				// Ignore removal failures, and throw original error
			}
			throw e;
		} finally {
			// Remove temp download
			logger.logDebug(`Removing temporary OS image download...`);
			await rimraf(tmpPath);
		}

		console.log('Done');
		return device.uuid;
	}


async configureOsImage(path: string, uuid: string, options: FlagsDef) {
		const configureCommand = ['os', 'configure', path, '--device', uuid];
		if (options.config) {
			configureCommand.push('--config', options.config);
		} else if (options.advanced) {
			configureCommand.push('--advanced');
		}
		await runCommand(configureCommand);
	}


async writeOsImage(path: string, deviceType: string, options: FlagsDef) {
		const osInitCommand = ['os', 'initialize', path, '--type', deviceType];
		if (options.yes) {
			osInitCommand.push('--yes');
		}
		if (options.drive) {
			osInitCommand.push('--drive', options.drive);
		}
		await runCommand(osInitCommand);
	}


@NgModule({
  imports: [
    BrowserModule, FormsModule, BsDropdownModule.forRoot(), AboutModalModule, ModalModule.forRoot(), TabsModule.forRoot(), TooltipModule.forRoot(),
    HighlightModule.forRoot({ theme: 'github' }), FileUploadModule, CardModule, DonutChartModule, ListModule, ToastNotificationListModule, SparklineChartModule,
    PaginationModule, ToolbarModule, WizardModule, AppRoutingModule, HttpClientModule
  ],
  declarations: [
    AppComponent, TimeAgoPipe, ConfirmDeleteDialogComponent, VerticalNavComponent, TestBarChartComponent, AdminPageComponent, DashboardPageComponent,
    ServicesPageComponent, ServiceDetailPageComponent, ImportersPageComponent, TestsPageComponent, TestCreatePageComponent, TestDetailPageComponent,
    TestRunnerPageComponent, OperationOverridePageComponent, ServiceRefsDialogComponent, ImporterWizardComponent, ArtifactUploaderDialogComponent,
    DynamicAPIDialogComponent, GenericResourcesDialogComponent, SecretsTabComponent, SnapshotsTabComponent, UsersTabComponent, HelpDialogComponent
  ],
  providers: [
    ConfigService, AuthenticationServiceProvider, BsDropdownConfig, NotificationService,
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthenticationHttpInterceptor,
      multi: true
    }
  ],
  entryComponents: [
    ServiceRefsDialogComponent, ImporterWizardComponent, ArtifactUploaderDialogComponent, DynamicAPIDialogComponent,
    GenericResourcesDialogComponent, HelpDialogComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }


export default function NextToNextServer(...args: any): PluginObj;


(options) => ({
  name: 'copy2',
  generateBundle() {
    if (!(options && options.assets && Array.isArray(options.assets))) {
      this.error('Plugin options are invalid')
    }
    const { assets, outputDirectory } = options
    let outputDir: string;
    if (outputDirectory) {
      outputDir = path.resolve(process.cwd(), outputDirectory);
    }
    if (!assets.length) {
      this.warn('An empty list of asstes was passed to plugin options')
      return
    }
    const srcDir = process.cwd()
    for (const asset of assets) {
      let srcFile: string
      let fileName: string
      if (typeof asset === 'string') {
        srcFile = asset
        fileName = asset
      } else if (Array.isArray(asset) && asset.length === 2) {
        srcFile = asset[0]
        fileName = asset[1]
      } else {
        this.error('Asset should be a string or a pair of strings [string, string]')
      }
      srcFile = path.normalize(srcFile)
      if (!path.isAbsolute(srcFile)) {
        srcFile = path.resolve(srcDir, srcFile)
      }
      glob(srcFile, {}, (err, files) => {
        if (err) {
          this.error(err);
        }
        if (!files || files.length === 0) {
          this.error(`"${srcFile}" doesn't exist`)
        } else if (files.length > 1 && Array.isArray(asset)) {
          this.error(`Cannot mix * pattern for assets with [string, string] notation`)
        } else {
          files.forEach((file) => {
            if (!Array.isArray(asset)) {
              fileName = path.relative(process.cwd(), file);
            }
            if (fs.statSync(file).isFile()) {
              const source = fs.readFileSync(file)
              if (outputDir) {
                const filePath = path.resolve(outputDir, fileName.replace(/\.\.\//gi, ''));
                mkdirp(path.dirname(filePath)).then(() => {
                  fs.writeFileSync(filePath, source);
                }).catch(this.error);
                fileName = path.relative(process.cwd(), filePath);
              }
              this.emitFile({
                fileName,
                source,
                type: 'asset',
              });
            }
          });
        }
      });
    }
  },
})


(err, files) => {
        if (err) {
          this.error(err);
        }
        if (!files || files.length === 0) {
          this.error(`"${srcFile}" doesn't exist`)
        } else if (files.length > 1 && Array.isArray(asset)) {
          this.error(`Cannot mix * pattern for assets with [string, string] notation`)
        } else {
          files.forEach((file) => {
            if (!Array.isArray(asset)) {
              fileName = path.relative(process.cwd(), file);
            }
            if (fs.statSync(file).isFile()) {
              const source = fs.readFileSync(file)
              if (outputDir) {
                const filePath = path.resolve(outputDir, fileName.replace(/\.\.\//gi, ''));
                mkdirp(path.dirname(filePath)).then(() => {
                  fs.writeFileSync(filePath, source);
                }).catch(this.error);
                fileName = path.relative(process.cwd(), filePath);
              }
              this.emitFile({
                fileName,
                source,
                type: 'asset',
              });
            }
          });
        }
      }


(file) => {
            if (!Array.isArray(asset)) {
              fileName = path.relative(process.cwd(), file);
            }
            if (fs.statSync(file).isFile()) {
              const source = fs.readFileSync(file)
              if (outputDir) {
                const filePath = path.resolve(outputDir, fileName.replace(/\.\.\//gi, ''));
                mkdirp(path.dirname(filePath)).then(() => {
                  fs.writeFileSync(filePath, source);
                }).catch(this.error);
                fileName = path.relative(process.cwd(), filePath);
              }
              this.emitFile({
                fileName,
                source,
                type: 'asset',
              });
            }
          }


() => {
                  fs.writeFileSync(filePath, source);
                }


interface IPluginCopy2Options {
  assets: CopyEntry[];
  outputDirectory?: string;
}


type CopyEntry = string | [string, string]


type RollupPluginCopy2 = (options: IPluginCopy2Options) => Plugin


generateBundle() {
    if (!(options && options.assets && Array.isArray(options.assets))) {
      this.error('Plugin options are invalid')
    }
    const { assets, outputDirectory } = options
    let outputDir: string;
    if (outputDirectory) {
      outputDir = path.resolve(process.cwd(), outputDirectory);
    }
    if (!assets.length) {
      this.warn('An empty list of asstes was passed to plugin options')
      return
    }
    const srcDir = process.cwd()
    for (const asset of assets) {
      let srcFile: string
      let fileName: string
      if (typeof asset === 'string') {
        srcFile = asset
        fileName = asset
      } else if (Array.isArray(asset) && asset.length === 2) {
        srcFile = asset[0]
        fileName = asset[1]
      } else {
        this.error('Asset should be a string or a pair of strings [string, string]')
      }
      srcFile = path.normalize(srcFile)
      if (!path.isAbsolute(srcFile)) {
        srcFile = path.resolve(srcDir, srcFile)
      }
      glob(srcFile, {}, (err, files) => {
        if (err) {
          this.error(err);
        }
        if (!files || files.length === 0) {
          this.error(`"${srcFile}" doesn't exist`)
        } else if (files.length > 1 && Array.isArray(asset)) {
          this.error(`Cannot mix * pattern for assets with [string, string] notation`)
        } else {
          files.forEach((file) => {
            if (!Array.isArray(asset)) {
              fileName = path.relative(process.cwd(), file);
            }
            if (fs.statSync(file).isFile()) {
              const source = fs.readFileSync(file)
              if (outputDir) {
                const filePath = path.resolve(outputDir, fileName.replace(/\.\.\//gi, ''));
                mkdirp(path.dirname(filePath)).then(() => {
                  fs.writeFileSync(filePath, source);
                }).catch(this.error);
                fileName = path.relative(process.cwd(), filePath);
              }
              this.emitFile({
                fileName,
                source,
                type: 'asset',
              });
            }
          });
        }
      });
    }
  }


@Injectable()
export class InvoiceService {
  constructor(private apiService: FutureApiService) { }

  public load(orderId: number, shareKey?: string): Observable<Invoice> {
    let apiOptions: ApiOptions = { loadingIndicator: true };
    if (shareKey) apiOptions = { ...apiOptions, overridingToken: shareKey };
    return this.apiService.get(Api.Orders, `order/invoiceData/${orderId}`, apiOptions);
  }
}


public load(orderId: number, shareKey?: string): Observable<Invoice> {
    let apiOptions: ApiOptions = { loadingIndicator: true };
    if (shareKey) apiOptions = { ...apiOptions, overridingToken: shareKey };
    return this.apiService.get(Api.Orders, `order/invoiceData/${orderId}`, apiOptions);
  }


async (
  publicKey: unknown,
  privateKey: unknown,
  algorithm: string,
  keyLength: number,
  apu: Uint8Array = new Uint8Array(0),
  apv: Uint8Array = new Uint8Array(0),
) => {
  if (!isCryptoKey(publicKey)) {
    throw new TypeError(invalidKeyInput(publicKey, 'CryptoKey'))
  }
  checkEncCryptoKey(publicKey, 'ECDH-ES')
  if (!isCryptoKey(privateKey)) {
    throw new TypeError(invalidKeyInput(privateKey, 'CryptoKey'))
  }
  checkEncCryptoKey(privateKey, 'ECDH-ES', 'deriveBits', 'deriveKey')

  const value = concat(
    lengthAndInput(encoder.encode(algorithm)),
    lengthAndInput(apu),
    lengthAndInput(apv),
    uint32be(keyLength),
  )

  if (!privateKey.usages.includes('deriveBits')) {
    throw new TypeError('ECDH-ES private key "usages" must include "deriveBits"')
  }

  const sharedSecret = new Uint8Array(
    await crypto.subtle.deriveBits(
      {
        name: 'ECDH',
        public: publicKey,
      },
      privateKey,
      Math.ceil(parseInt((<EcKeyAlgorithm>privateKey.algorithm).namedCurve.substr(-3), 10) / 8) <<
        3,
    ),
  )

  return concatKdf(digest, sharedSecret, keyLength, value)
}


async (key: unknown) => {
  if (!isCryptoKey(key)) {
    throw new TypeError(invalidKeyInput(key, 'CryptoKey'))
  }

  return (<{ publicKey: CryptoKey; privateKey: CryptoKey }>(
    await crypto.subtle.generateKey(
      { name: 'ECDH', namedCurve: (<EcKeyAlgorithm>key.algorithm).namedCurve },
      true,
      ['deriveBits'],
    )
  )).privateKey
}


(key: unknown) => {
  if (!isCryptoKey(key)) {
    throw new TypeError(invalidKeyInput(key, 'CryptoKey'))
  }
  return ['P-256', 'P-384', 'P-521'].includes((<EcKeyAlgorithm>key.algorithm).namedCurve)
}


export function endsWith(tail: string, seq: string): boolean;


export function endsWith<A>(tail: A | A[], seq: A[]): boolean;


export function endsWith(tail: string): (seq: string) => boolean;


export function endsWith<A>(tail: A | A[]): (seq: A[]) => boolean;


export function endsWith(tail: any, seq?: any) {
  if (arguments.length === 1) {
    return (theSeq: any) => endsWith(tail, theSeq as any);
  }

  const _tail = getValue(tail);
  const _seq = getValue(seq);
  if (isNil(_tail) || isNil(_seq)) return false;

  if (typeof _tail === "string" && typeof _seq === "string") {
    return _seq.endsWith(_tail);
  } else if (_tail instanceof Array && !(_seq[0] instanceof Array)) {
    let j = _seq.length - 1;
    for (let i = _tail.length - 1; i > 0; --i, --j) {
      if (!equals(_tail[i], _seq[j])) return false;
    }
    return true;
  } else {
    return equals(_seq[_seq.length - 1], _tail);
  }
}


(theSeq: any) => endsWith(tail, theSeq as any)


() => {
  it('registered the service', () => {
    const service = app.service('books');

    assert.ok(service, 'Registered the service');
  });
}


() => {
    const service = app.service('books');

    assert.ok(service, 'Registered the service');
  }


export default async function getCerts(client: Client, next?: number) {
  let certsUrl = `/v4/now/certs?limit=20`;

  if (next) {
    certsUrl += `&until=${next}`;
  }

  return await client.fetch<Response>(certsUrl);
}


type Response = {
  certs: Cert[];
  pagination: PaginationOptions;
};


(): JSX.Element => (
  <Document>
    <QuestionnaireBuilder
      questionnaire={


export class Metadata {
    public name: string;
    public predicate?: Function;
    public nameDeserializationHandlers?: Array<NameDeserializationHandler>;
    public nameSerializationHandlers?: Array<NameSerializationHandler>;
    public dataDeserializationHandlers?: Array<DataHandler>;
    public dataSerializationHandlers?: Array<DataHandler>;
    public type?: Function;
    public typeName: string;

    public constructor() {

    }
}


export type NameDeserializationHandler = (parent: any, metadata: Metadata, keyOptions: Array<string>) => string;


export type NameSerializationHandler = (parent: any, metadata: Metadata) => string;


export type DataHandler = (data: any) => any;


@Module({
  imports: [TypeOrmModule.forFeature([User, Profile])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [TypeOrmModule],
})
export class UsersModule {}


() => {
  beforeEach(() => {
    this.store = {};
  });

  it('renders', () => {
    const wrapper = shallow(<PublishButton appState={this.store} />);
    expect(wrapper).toMatchSnapshot();
  });

  it('toggles the auth dialog on click if not authed', () => {
    this.store.toggleAuthDialog = jest.fn();

    const wrapper = shallow(<PublishButton appState={this.store} />);
    wrapper.find('button').simulate('click');
    expect(this.store.toggleAuthDialog).toHaveBeenCalled();
  });

  it('toggles the publish method on click if authed', async () => {
    this.store.gitHubToken = 'github-token';

    const wrapper = shallow(<PublishButton appState={this.store} />);
    const instance: PublishButton = wrapper.instance() as any;
    instance.publishFiddle = jest.fn();
    await instance.handleClick();

    expect(instance.publishFiddle).toHaveBeenCalled();
  });

  it('attempts to publish to Gist', async () => {
    const mockOctokit = {
      authenticate: jest.fn(),
      gists: {
        create: jest.fn(async () => ({ data: { id: '123' } }))
      }
    };

    (getOctokit as any).mockReturnValue(mockOctokit);

    const wrapper = shallow(<PublishButton appState={this.store} />);
    const instance: PublishButton = wrapper.instance() as any;

    await instance.publishFiddle();

    expect(mockOctokit.authenticate).toHaveBeenCalled();
    expect(mockOctokit.gists.create).toHaveBeenCalledWith({
      description: 'Electron Fiddle Gist',
      files: {
        'index.html': { content: 'html-content' },
        'renderer.js': { content: 'renderer-content' },
        'main.js': { content: 'main-content' },
      },
      public: true
    });
  });

  it('handles an error in Gist publishing', async () => {
    const mockOctokit = {
      authenticate: jest.fn(),
      gists: {
        create: jest.fn(() => {
          throw new Error('bwap bwap');
        })
      }
    };

    (getOctokit as any).mockReturnValue(mockOctokit);

    const wrapper = shallow(<PublishButton appState={this.store} />);
    const instance: PublishButton = wrapper.instance() as any;

    await instance.publishFiddle();

    expect(mockOctokit.authenticate).toHaveBeenCalled();
    expect(wrapper.state('isPublishing')).toBe(false);
  });
}


() => {
    this.store = {};
  }


() => {
    const wrapper = shallow(<PublishButton appState={this.store} />);
    expect(wrapper).toMatchSnapshot();
  }


() => {
    this.store.toggleAuthDialog = jest.fn();

    const wrapper = shallow(<PublishButton appState={this.store} />);
    wrapper.find('button').simulate('click');
    expect(this.store.toggleAuthDialog).toHaveBeenCalled();
  }


async () => {
    this.store.gitHubToken = 'github-token';

    const wrapper = shallow(<PublishButton appState={this.store} />);
    const instance: PublishButton = wrapper.instance() as any;
    instance.publishFiddle = jest.fn();
    await instance.handleClick();

    expect(instance.publishFiddle).toHaveBeenCalled();
  }


async () => {
    const mockOctokit = {
      authenticate: jest.fn(),
      gists: {
        create: jest.fn(async () => ({ data: { id: '123' } }))
      }
    };

    (getOctokit as any).mockReturnValue(mockOctokit);

    const wrapper = shallow(<PublishButton appState={this.store} />);
    const instance: PublishButton = wrapper.instance() as any;

    await instance.publishFiddle();

    expect(mockOctokit.authenticate).toHaveBeenCalled();
    expect(mockOctokit.gists.create).toHaveBeenCalledWith({
      description: 'Electron Fiddle Gist',
      files: {
        'index.html': { content: 'html-content' },
        'renderer.js': { content: 'renderer-content' },
        'main.js': { content: 'main-content' },
      },
      public: true
    });
  }


async () => ({ data: { id: '123' } })


async () => {
    const mockOctokit = {
      authenticate: jest.fn(),
      gists: {
        create: jest.fn(() => {
          throw new Error('bwap bwap');
        })
      }
    };

    (getOctokit as any).mockReturnValue(mockOctokit);

    const wrapper = shallow(<PublishButton appState={this.store} />);
    const instance: PublishButton = wrapper.instance() as any;

    await instance.publishFiddle();

    expect(mockOctokit.authenticate).toHaveBeenCalled();
    expect(wrapper.state('isPublishing')).toBe(false);
  }


() => {
          throw new Error('bwap bwap');
        }


error => this.props.onError(error)


() => {
        const { data, isValid } = this.activePaymentMethod;

        if (!isValid) {
            this.showValidation();
            return false;
        }

        return this.props.onSubmit({ data, isValid }, this);
    }


state => this.props.onAdditionalDetails(state, this)


class DropinElement extends UIElement<DropinElementProps> {
    public static type = 'dropin';
    protected static defaultProps = defaultProps;
    public dropinRef = null;

    constructor(props) {
        super(props);
        this.submit = this.submit.bind(this);
    }

    get isValid() {
        return !!this.dropinRef && !!this.dropinRef.state.activePaymentMethod && !!this.dropinRef.state.activePaymentMethod.isValid;
    }

    showValidation() {
        if (this.dropinRef.state.activePaymentMethod) {
            this.dropinRef.state.activePaymentMethod.showValidation();
        }

        return this;
    }

    setStatus(status, props = {}) {
        this.dropinRef.setStatus({ type: status, props });
        return this;
    }

    get activePaymentMethod() {
        if (!this.dropinRef.state && !this.dropinRef.state.activePaymentMethod) {
            return null;
        }

        return this.dropinRef.state.activePaymentMethod;
    }

    get data() {
        if (!this.activePaymentMethod) {
            return null;
        }

        return this.dropinRef.state.activePaymentMethod.data;
    }

    /**
     * Calls the onSubmit event with the state of the activePaymentMethod
     */
    submit(): void {
        if (!this.activePaymentMethod) {
            throw new Error('No active payment method.');
        }

        this.activePaymentMethod
            .startPayment()
            .then(this.handleSubmit)
            .catch(error => this.props.onError(error));
    }

    protected handleSubmit = () => {
        const { data, isValid } = this.activePaymentMethod;

        if (!isValid) {
            this.showValidation();
            return false;
        }

        return this.props.onSubmit({ data, isValid }, this);
    };

    handleAction(action: PaymentAction) {
        if (!action || !action.type) throw new Error('Invalid Action');

        if (this.activePaymentMethod.updateWithAction) {
            return this.activePaymentMethod.updateWithAction(action);
        }

        // Extract desired props that we need to pass on from the pmConfiguration for this particular PM
        const pmConfig = getComponentConfiguration(action.paymentMethodType, this.props.paymentMethodsConfiguration);

        const paymentAction: UIElement = this.props.createFromAction(action, {
            isDropin: true,
            onAdditionalDetails: state => this.props.onAdditionalDetails(state, this),
            onError: this.props.onError, // Add ref to onError in case the merchant has defined one in the component options
            ...(pmConfig?.onError && { onError: pmConfig.onError }) // Overwrite ref to onError in case the merchant has defined one in the pmConfig options
        });

        if (paymentAction) {
            return this.setStatus(paymentAction.props.statusType, { component: paymentAction });
        }

        return null;
    }

    render() {
        return (
            <CoreProvider i18n={this.props.i18n} loadingContext={this.props.loadingContext}>
                <DropinComponent
                    {...this.props}
                    onChange={this.setState}
                    onSubmit={this.handleSubmit}
                    elementRef={this.elementRef}
                    ref={dropinRef => {
                        this.dropinRef = dropinRef;
                    }}


showValidation() {
        if (this.dropinRef.state.activePaymentMethod) {
            this.dropinRef.state.activePaymentMethod.showValidation();
        }

        return this;
    }


setStatus(status, props = {}) {
        this.dropinRef.setStatus({ type: status, props });
        return this;
    }


/**
     * Calls the onSubmit event with the state of the activePaymentMethod
     */
submit(): void {
        if (!this.activePaymentMethod) {
            throw new Error('No active payment method.');
        }

        this.activePaymentMethod
            .startPayment()
            .then(this.handleSubmit)
            .catch(error => this.props.onError(error));
    }


handleAction(action: PaymentAction) {
        if (!action || !action.type) throw new Error('Invalid Action');

        if (this.activePaymentMethod.updateWithAction) {
            return this.activePaymentMethod.updateWithAction(action);
        }

        // Extract desired props that we need to pass on from the pmConfiguration for this particular PM
        const pmConfig = getComponentConfiguration(action.paymentMethodType, this.props.paymentMethodsConfiguration);

        const paymentAction: UIElement = this.props.createFromAction(action, {
            isDropin: true,
            onAdditionalDetails: state => this.props.onAdditionalDetails(state, this),
            onError: this.props.onError, // Add ref to onError in case the merchant has defined one in the component options
            ...(pmConfig?.onError && { onError: pmConfig.onError }) // Overwrite ref to onError in case the merchant has defined one in the pmConfig options
        });

        if (paymentAction) {
            return this.setStatus(paymentAction.props.statusType, { component: paymentAction });
        }

        return null;
    }


render() {
        return (
            <CoreProvider i18n={this.props.i18n} loadingContext={this.props.loadingContext}>
                <DropinComponent
                    {...this.props}
                    onChange={this.setState}
                    onSubmit={this.handleSubmit}
                    elementRef={this.elementRef}
                    ref={dropinRef => {
                        this.dropinRef = dropinRef;
                    }}


@Module({
  providers: [CaslAbilityFactory],
  exports: [CaslAbilityFactory],
})
export class CaslModule {}


() => {
  const type = select(
    'type',
    ['button', 'submit', 'reset'],
    undefined
  );

  const appearance = select(
    'appearance',
    ['basic', 'primary', 'success', 'alert', 'transparent'],
    undefined
  );

  const size = select(
    'size',
    ['tiny', 'regular', 'large'],
    undefined
  );

  const disabled = boolean(
    'disabled',
    false
  );

  const expanded = boolean(
    'expanded',
    false
  );

  const loading = boolean(
    'loading',
    false
  );

  const icon = text(
    'icon',
    ''
  );

  const iconAlign = select(
    'iconAlign',
    ['left', 'right'],
    undefined
  );

  const children = text(
    'children',
    'Button'
  );

  return (
    <Button
      onClick={action('button-clicked')}
      onMouseEnter={action('mouse-enter')}
      onMouseLeave={action('mouse-leave')}
      type={type}
      appearance={appearance}
      size={size}
      expanded={expanded}
      disabled={disabled}
      loading={loading}
      icon={icon}
      iconAlign={iconAlign}
    >
      {children}
    </Button>


action('button-clicked')


action('mouse-enter')


action('mouse-leave')


/**
 * The Fade transition is used by the [Modal](https://mui.com/components/modal/) component.
 * It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
 *
 * Demos:
 *
 * - [Transitions](https://mui.com/components/transitions/)
 *
 * API:
 *
 * - [Fade API](https://mui.com/api/fade/)
 * - inherits [Transition API](http://reactcommunity.org/react-transition-group/transition/#Transition-props)
 */
export default function Fade(props: FadeProps): JSX.Element;


export interface FadeProps extends Omit<TransitionProps, 'children'> {
  /**
   * Perform the enter transition when it first mounts if `in` is also `true`.
   * Set this to `false` to disable this behavior.
   * @default true
   */
  appear?: boolean;
  /**
   * A single child content element.
   */
  children: React.ReactElement<any, any>;
  /**
   * The transition timing function.
   * You may specify a single easing or a object containing enter and exit values.
   */
  easing?: TransitionProps['easing'];
  /**
   * If `true`, the component will transition in.
   */
  in?: boolean;
  ref?: React.Ref<unknown>;
  /**
   * The duration for the transition, in milliseconds.
   * You may specify a single timeout for all transitions, or individually with an object.
   * @default {
   *   enter: duration.enteringScreen,
   *   exit: duration.leavingScreen,
   * }
   */
  timeout?: TransitionProps['timeout'];
}


/*!
 * @license
 * Copyright 2016 Alfresco Software, Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
export declare function getType(type: any): any;


function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ChakraProvider theme={theme}>
      <Component {...pageProps} />
    </ChakraProvider>
  );
}


({ children }) => {
  return (
    <div className="antares-container">
      <AntaresActions></AntaresActions>
      <AntaresSidebar></AntaresSidebar>
      {children}
    </div>


function generateSampleMessage<T extends object>(instance: T) {
    const filledObject = (instance.constructor as typeof protobuf.Message)
        .toObject(instance as protobuf.Message<T>, {defaults: true});
    return (instance.constructor as typeof protobuf.Message).fromObject(filledObject) as T;
}


function stubSimpleCall<ResponseType>(response?: ResponseType, error?: Error) {
    return error ? sinon.stub().rejects(error) : sinon.stub().resolves([response]);
}


function stubSimpleCallWithCallback<ResponseType>(response?: ResponseType, error?: Error) {
    return error ? sinon.stub().callsArgWith(2, error) : sinon.stub().callsArgWith(2, null, response);
}


function stubPageStreamingCall<ResponseType>(responses?: ResponseType[], error?: Error) {
    const pagingStub = sinon.stub();
    if (responses) {
        for (let i = 0; i < responses.length; ++i) {
            pagingStub.onCall(i).callsArgWith(2, null, responses[i]);
        }
    }
    const transformStub = error ? sinon.stub().callsArgWith(2, error) : pagingStub;
    const mockStream = new PassThrough({
        objectMode: true,
        transform: transformStub,
    });
    // trigger as many responses as needed
    if (responses) {
        for (let i = 0; i < responses.length; ++i) {
            setImmediate(() => { mockStream.write({}); });
        }
        setImmediate(() => { mockStream.end(); });
    } else {
        setImmediate(() => { mockStream.write({}); });
        setImmediate(() => { mockStream.end(); });
    }
    return sinon.stub().returns(mockStream);
}


function stubAsyncIterationCall<ResponseType>(responses?: ResponseType[], error?: Error) {
    let counter = 0;
    const asyncIterable = {
        [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++]});
                }
            };
        }
    };
    return sinon.stub().returns(asyncIterable);
}


() => { mockStream.write({}); }


() => { mockStream.end(); }


() => {
        const servicePath = assetserviceModule.v1p5beta1.AssetServiceClient.servicePath;
        assert(servicePath);
    }


() => {
        const apiEndpoint = assetserviceModule.v1p5beta1.AssetServiceClient.apiEndpoint;
        assert(apiEndpoint);
    }


() => {
        const port = assetserviceModule.v1p5beta1.AssetServiceClient.port;
        assert(port);
        assert(typeof port === 'number');
    }


() => {
        const client = new assetserviceModule.v1p5beta1.AssetServiceClient();
        assert(client);
    }


() => {
        const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
            fallback: true,
        });
        assert(client);
    }


async () => {
        const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
              credentials: {client_email: 'bogus', private_key: 'bogus'},
              projectId: 'bogus',
        });
        assert.strictEqual(client.assetServiceStub, undefined);
        await client.initialize();
        assert(client.assetServiceStub);
    }


() => {
        const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
              credentials: {client_email: 'bogus', private_key: 'bogus'},
              projectId: 'bogus',
        });
        client.close();
    }


async () => {
        const fakeProjectId = 'fake-project-id';
        const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
              credentials: {client_email: 'bogus', private_key: 'bogus'},
              projectId: 'bogus',
        });
        client.auth.getProjectId = sinon.stub().resolves(fakeProjectId);
        const result = await client.getProjectId();
        assert.strictEqual(result, fakeProjectId);
        assert((client.auth.getProjectId as SinonStub).calledWithExactly());
    }


async () => {
        const fakeProjectId = 'fake-project-id';
        const client = new assetserviceModule.v1p5beta1.AssetServiceClient({
              credentials: {client_email: 'bogus', private_key: 'bogus'},
              projectId: 'bogus',
        });
        client.auth.getProjectId = sinon.stub().callsArgWith(0, null, fakeProjectId);
        const promise = new Promise((resolve, reject) => {
            client.getProjectId((err?: Error|null, projectId?: string|null) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(projectId);
                }
            });
        });
        const result = await promise;
        assert.strictEqual(result, fakeProjectId);
    }


(resolve, reject) => {
            client.getProjectId((err?: Error|null, projectId?: string|null) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(projectId);
                }
            });
        }


(err?: Error|null, projectId?: string|null) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(projectId);
                }
            }