Skip to content

Commit

Permalink
canvas2svg.js translation; better inference based on type+format+url
Browse files Browse the repository at this point in the history
Signed-off-by: eternal-flame-AD <[email protected]>
  • Loading branch information
eternal-flame-AD committed Sep 3, 2024
1 parent 3f38ef7 commit b1def1e
Showing 1 changed file with 216 additions and 67 deletions.
283 changes: 216 additions & 67 deletions js/igv.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@

// allow extra keys
type ExtraKeys<T> = T & { [key: string]: any };

type Override<T, R> = Omit<T, keyof R> & R;

// either uppercase or lowercase
type AnyCase<T extends string> = Uppercase<T> | Lowercase<T>;

// any action that can be deferred as a promise or a thenable
type Deferrable<T> = T | Promise<T> | Thenable<T, any> | (() => T | Promise<T> | Thenable<T, any>);

export interface Thenable<T, E> {
then(resolve: (value: T) => void, reject: (error: E) => void): void;
}
Expand All @@ -21,24 +19,99 @@ export type TrackType =
"snp" | "eqtl";

export type TrackLoad<T extends TrackType> =
Override<Tracks.TrackCommonOptions,
(T extends "annotation" ? (Tracks.AnnotationTrackOptions & Tracks.TypeFormatPair<'annotation', Tracks.AnnotationFormat>) :
T extends "wig" ? (Tracks.WigTrackOptions & Tracks.TypeFormatPair<'wig', Tracks.WigFormat>) :
T extends "alignment" ? (Tracks.AlignmentTrackOptions & Tracks.TypeFormatPair<'alignment', Tracks.AlignmentFormat>) :
T extends "variant" ? (Tracks.VariantTrackOptions & Tracks.TypeFormatPair<'variant', Tracks.VariantFormat>) :
T extends "mut" ? (Tracks.MutationTrackOptions & Tracks.TypeFormatPair<'mut', Tracks.MutationFormat>) :
T extends "seg" ? (Tracks.SegTrackOptions & Tracks.TypeFormatPair<'seg', Tracks.SegFormat>) :
T extends "gwas" ? (Tracks.GWASTrackOptions<Tracks.GWASFormat> & Tracks.TypeFormatPair<'gwas', Tracks.GWASFormat>) :
T extends "interact" | "interaction" ? (Tracks.InteractTrackOptions & Tracks.TypeFormatPair<'interact' | 'interaction', Tracks.InteractFormat>) :
T extends "qtl" ? (Tracks.QTLTrackOptions & Tracks.TypeFormatPair<'qtl', Tracks.QTLFormat>) :
T extends "junction" ? (Tracks.JunctionTrackOptions & Tracks.TypeFormatPair<'junction', Tracks.JunctionFormat>) :
T extends "cnvpytor" ? (Tracks.CnvPyTorTrackOptions & Tracks.TypeFormatPair<'cnvpytor', Tracks.CnvPyTorFormat>) :
T extends "merged" ? (Tracks.WigMergedTrackOptions & { type: 'merged' }) :
T extends "arc" ? (Tracks.ArcTrackOptions & Tracks.TypeFormatPair<'arc', Tracks.ArcFormat>) :
/* undocumented options */
T extends "snp" ? (Tracks.SNPTrackOptions & Tracks.TypeFormatPair<'snp', Tracks.SNPFormat>) :
T extends "eqtl" ? (Tracks.EQTLTrackOptions & Tracks.TypeFormatPair<'eqtl', Tracks.EQTLFormat>) :
never)>;
Tracks.TrackCommonOptions &
(T extends "annotation" ? Tracks.AnnotationTrackOptions & TypeFormatPair<'annotation'> :
T extends "wig" ? Tracks.WigTrackOptions & TypeFormatPair<'wig'> :
T extends "alignment" ? Tracks.AlignmentTrackOptions & TypeFormatPair<'alignment'> :
T extends "variant" ? Tracks.VariantTrackOptions & TypeFormatPair<'variant'> :
T extends "mut" ? Tracks.MutationTrackOptions & TypeFormatPair<'mut'> :
T extends "seg" ? Tracks.SegTrackOptions & TypeFormatPair<'seg'> :
T extends "gwas" ? Tracks.GWASTrackOptions<Tracks.GWASFormat> & TypeFormatPair<'gwas'> :
T extends "interact" | "interaction" ? Tracks.InteractTrackOptions & TypeFormatPair<'interact' | 'interaction'> :
T extends "qtl" ? Tracks.QTLTrackOptions & TypeFormatPair<'qtl'> :
T extends "junction" ? Tracks.JunctionTrackOptions & TypeFormatPair<'junction'> :
T extends "cnvpytor" ? Tracks.CnvPyTorTrackOptions & TypeFormatPair<'cnvpytor'> :
T extends "merged" ? Tracks.WigMergedTrackOptions & { type: 'merged' } :
T extends "arc" ? Tracks.ArcTrackOptions & TypeFormatPair<'arc'> :
/* undocumented options */
T extends "snp" ? Tracks.SNPTrackOptions & TypeFormatPair<'snp'> :
T extends "eqtl" ? Tracks.EQTLTrackOptions & TypeFormatPair<'eqtl'> :
never);

export type TypeFormatPair<T extends TrackType> = {
type?: T;
format: TrackFormatOf<T>;
url?: string;
} | {
type?: T;
format?: TrackFormatOf<T>;
url: TrackFormatOf<T> extends string ? Deferrable<URLInference.URLWithExtension<TrackFormatOf<T>>> : never;
} | ({
type?: T;
format?: never;
url?: never;
} & (CustomReaderOf<T> | ManualFeatureOf<T>)) | {
type: T;
format?: TrackFormatOf<T>;
// if there is only one possible format, we permit it to be omitted
url: TrackFormatOf<T> extends string ? (string extends TrackFormatOf<T> ? never : string) : never;
};

export type TrackFormatOf<T extends TrackType> =
T extends "annotation" ? Tracks.AnnotationFormat :
T extends "wig" ? Tracks.WigFormat :
T extends "alignment" ? Tracks.AlignmentFormat :
T extends "variant" ? Tracks.VariantFormat :
T extends "mut" ? Tracks.MutationFormat :
T extends "seg" ? Tracks.SegFormat :
T extends "gwas" ? Tracks.GWASFormat :
T extends "interact" | 'interaction' ? Tracks.InteractFormat :
T extends "qtl" ? Tracks.QTLFormat :
T extends "junction" ? Tracks.JunctionFormat :
T extends "cnvpytor" ? Tracks.CnvPyTorFormat :
T extends "arc" ? Tracks.ArcFormat :
/* undocumented options */
T extends "snp" ? Tracks.SNPFormat :
T extends "eqtl" ? Tracks.EQTLFormat :
never;

export type ManualFeatureOf<T extends TrackType> =
T extends "annotation" ? {
features: Record<string, any>[];
} :
T extends "wig" ? {
features: Tracks.WigTrackManualFeatures;
} :
T extends "seg" ? {
features: Array<{
chr: string;
start: number;
end: number;
value: number;
sample: string;
}>;
} : never;

export type CustomReaderOf<T extends TrackType> =
T extends "annotation" ? { reader: Tracks.AnnotationCustomReader } |
{
source: {
url: string;
method?: "GET" | "POST";
contentType?: string;
body?: string;
}
} :
T extends "seg" ? {
source: {
url: (options: { chr: string }) => string;
method?: "GET" | "POST";
contentType?: string;
body?: string;
mappings: Record<string, string>;
}
} :
never;

export type TrackOf<T extends TrackType> =
T extends "annotation" ? Tracks.Track :
Expand Down Expand Up @@ -68,22 +141,34 @@ export type StaticFeatureConfig<T extends Record<string, any>> = {
mappings: Record<keyof T, string>;
}

// ref: https://github.com/igvteam/igv-utils/blob/master/src/fileUtils.js
export namespace URLInference {
type StripLast<S extends string, SEP extends string> = S extends `${infer _}${SEP}${infer P}` ? StripLast<P, SEP> : S;
type StripQuery<S extends string> = S extends `${infer P}?${infer _}` ? P : S;
type StripSuffix<S extends string, T extends string> = S extends `${infer P}.${T}` ? P : S;

export type FilenameOfURL<U extends string> = StripLast<StripQuery<U>, '/'>

type AuxExtensions = ".gz" | ".tab" | ".txt" | ".bgz";

export type InferExtension<F extends string> = Lowercase<F> extends `${infer B}${AuxExtensions}` ? StripLast<B, '.'> : StripLast<Lowercase<F>, '.'>

type Query = `?${string}`;

export type URLWithExtension<E extends string> =
`${string}.${E}${AuxExtensions | ''}${Query | ''}`;
}

export namespace Tracks {
export class Track {
public readonly id: string;
public readonly type: string;
public readonly name?: string;
}

export interface TypeFormatPair<K extends string, F extends string> {
type?: K;
format?: F;
}

export interface TrackCommonOptions {
name?: string;
url?: string | Promise<string> | (() => string | Promise<string>) |
Thenable<string, any> | (() => Thenable<string, any>);
indexURL?: string | Promise<string>;
indexed?: false;
order?: number;
Expand All @@ -97,6 +182,12 @@ export namespace Tracks {
oauthToken?: string | (() => string | Promise<string>);
sourceType?: string;
filename?: string;
roi?: DefineROI[];
}

export class AnnotationCustomReader {
constructor(config: TrackCommonOptions & AnnotationTrackOptions);
readFeatures(chr: string, start: number, end: number): Promise<Record<string, any>[]>;
}

export type AnnotationFormat = "bed" | "gff3" | "gtf" |
Expand Down Expand Up @@ -230,11 +321,11 @@ export namespace Tracks {
colorTable?: Record<string, string>,
}

export type AnnotationTrackOptions = ExtraKeys<AnnotationTrackDisplay & AnnotationTrackSearch & AnnotationTrackCommonOptions>;
export type AnnotationTrackOptions = AnnotationTrackDisplay & AnnotationTrackSearch & AnnotationTrackCommonOptions;

export type WigFormat = "wig" | "bigWig" | "bigwig";
export type WigFormat = AnyCase<"wig"> | "bigWig" | AnyCase<'bigWig'> | AnyCase<'tdf'> | AnyCase<'bw'>;

export interface WigTrackCommonOptions {
export interface WigTrackOptions {
/**
* Autoscale track to maximum value in view
*
Expand Down Expand Up @@ -305,13 +396,20 @@ export namespace Tracks {
windowFunction?: "mean" | "max" | "min";

height?: number;

displayMode?: AnyCase<"EXPANDED" | "SQUISHED" | "COLLAPSED">;
}

export type WigTrackOptions = ExtraKeys<WigTrackCommonOptions>;
export type WigTrackManualFeatures = {
chr: string;
start: number;
end: number;
value: number;
}[];

export type WigMergedTrackOptions = ExtraKeys<{
tracks: (Partial<TrackCommonOptions> & ((TypeFormatPair<'wig', WigFormat> & WigTrackOptions) | (TypeFormatPair<'junction', JunctionFormat> & JunctionTrackOptions)))[];
}>;
export type WigMergedTrackOptions = {
tracks: (Partial<TrackCommonOptions> & ((TypeFormatPair<'wig'> & WigTrackOptions) | (TypeFormatPair<'junction'> & JunctionTrackOptions)))[];
};

export type VariantFormat = "vcf";

Expand All @@ -326,7 +424,7 @@ export namespace Tracks {
info: Record<string, string>;
}

export type VariantTrackOptions = ExtraKeys<{
export type VariantTrackOptions = {
displayMode?: AnyCase<"EXPANDED" | "SQUISHED" | "COLLAPSED">;
squishedCallHeight?: number;
expandedCallHeight?: number;
Expand All @@ -337,7 +435,11 @@ export namespace Tracks {
homvarColor?: string;
hetvarColor?: string;
homrefColor?: string;
}>;
supportsWholeGenome?: boolean;
showGenotypes?: boolean;
strokecolor?: (variant: VCFItem) => string | void;
context_hook?: (variant: VCFItem, ctx: Draw.CanvasContext, x: number, y: number, w: number, h: number) => void;
};

export type CnvPyTorFormat = "pytor" | "vcf";

Expand Down Expand Up @@ -454,34 +556,7 @@ export namespace Tracks {
} | {
log?: boolean;
isLog?: never;
})
& (
{
features: Array<{
chr: string;
start: number;
end: number;
value: number;
sample: string;
}>;
url?: never;
source?: never;
} | ({
features?: never;
source?: never;
} & Required<Pick<TrackCommonOptions, 'url'>>) |
{
source: StaticFeatureConfig<{
chr: string;
value: number;
sampleKey: string;
sample: string;
}>;
features?: never;
url?: never;
}

)
});


export type InteractFormat = "interact" | "bedpe" | "bigInteract" | "bb";
Expand Down Expand Up @@ -742,6 +817,7 @@ export type CreateOpt = (GenomeOpt & CreateOptExtras) | (
export interface ROISet {
url: string;
name: string;
indexed: boolean;
isUserDefined: boolean;
color: string;
headerColor: string;
Expand Down Expand Up @@ -853,6 +929,79 @@ export type IGV = {
readonly version: () => string;
}

declare namespace Draw {
// ref: canvas2svg.js
export class CanvasContext {
public readonly canvas: this;
public readonly isSVG: boolean;

public strokeStyle: string;
public lineWidth: number;

private constructor(config: {
ctx?: any;
width?: number;
height?: number;
enableMirroring?: boolean;
viewBox?: {
x: number;
y: number;
width: number;
height: number;
};
multiLocusGap?: any;
backdropColor?: string;
document?: Document;
});
setWidth(width: number): void;
setHeight(height: number): void;
getSerializedSvg(fixNamedEntitles?: boolean): string;
getSvg(): SVGSVGElement;
saveWithTranslationAndClipRect(id: string, tx: number, ty: number, width: number, height: number, clipYOffset: number): void;
save(): void;
restore(): void;
addTrackGroupWithTranslationAndClipRect(
id: string,
tx: number,
ty: number,
width: number,
height: number,
clipYOffset: number,
)
scale(x: number, y: number): void;
rotate(angle: number): void;
translate(x: number, y: number): void;
transform(a: number, b: number, c: number, d: number, e: number, f: number): void;
beginPath(): void;
moveTo(x: number, y: number): void;
closePath(): void;
lineTo(x: number, y: number): void;
bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void;
quadraticCurveTo(cp1x: number, cp1y: number, x: number, y: number): void;
arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void;
stroke(): void;
fill(): void;
rect(x: number, y: number, w: number, h: number): void;
fillRect(x: number, y: number, w: number, h: number): void;
strokeRect(x: number, y: number, w: number, h: number): void;
strokeEllipse(cx: number, cy: number, rx: number, ry: number,
rotation: number, startAngle: number, endAngle: number,
isCCW: boolean): void;
fillEllipse(cx: number, cy: number, rx: number, ry: number,
rotation: number, startAngle: number, endAngle: number,
isCCW: boolean): void;
clearRect(x: number, y: number, w: number, h: number): void;
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient;
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
strokeText(text: string, x: number, y: number): void;
measureText(text: string): TextMetrics;
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void;
clip(): void;
drawImage();
createPattern(image: CanvasImageSource, repetition?: string): CanvasPattern;
}
}

declare const igv: IGV;

export default igv;

0 comments on commit b1def1e

Please sign in to comment.