Using jexl callbacks
TL;DR: a config callback is a string prefixed with jexl:. Read feature
attributes as plain properties (feature.strand). When an expression outgrows
one line, register your own function from a small plugin and call it like a
built-in.
A callback is a Jexl expression in a slot
that takes one, "color": "jexl:feature.strand==-1?'red':'blue'". Any feature
attribute is a plain property, nested attributes work (feature.INFO.SVTYPE),
and feature.parent is the parent feature:
jexl: feature.start // start coordinate, 0-based half open
jexl: feature.end // end coordinate, 0-based half open
jexl: feature.refName // chromosome or reference sequence name
jexl: feature.CIGAR // BAM or CRAM feature CIGAR string
jexl: feature.seq // BAM or CRAM feature sequence
jexl: feature.type // feature type e.g. mRNA or gene
jexl: feature.id // the feature's id attribute, e.g. a GFF3 ID=
jexl: feature.parent // parent feature, e.g. the gene of an mRNA (undefined if none)
The cookbook has the common expressions (a lookup table
by type, a threshold, a gradient, a label with a fallback), and the
"Jexl callback examples" track
on the hosted demo combines a lookup-table color with a template-string
mouseover. formatDetails is the one slot family whose callback returns an
object, one key per row
(customizing feature details).
Property access vs get()
feature.start and get(feature,'start') are equivalent; get() also works on
older JBrowse releases. What feature is depends on the callback:
| Callback | feature is | Property form | get() form |
|---|---|---|---|
Color, label, tooltip, filter (color, name, mouseover, filterBy) | a SimpleFeature | yes | yes |
formatDetails | a plain object from the session | yes | no |
In JavaScript plugin code a SimpleFeature handed to your own function is the
real object, read with feature.get('start').
Functions
Beyond the feature's own properties, jexl has these functions. getTag smooths
over the differences between BAM and CRAM features to reach their tags.
Math functions
jexl: max(0, 2)
jexl: min(0, 2)
jexl: sqrt(4)
jexl: ceil(0.5)
jexl: floor(0.5)
jexl: round(0.5)
jexl: abs(-0.5)
jexl: log10(50000)
jexl: parseInt('2')
jexl: parseFloat('2.054')
String functions
jexl: charAt('abc', 2) // c
jexl: charCodeAt(' ', 0) // 32
jexl: codePointAt(' ', 0) // 32
jexl: startsWith('kittycat', 'kit') // true
jexl: endsWith('kittycat', 'cat') // true
jexl: padStart('cat', 8, 'kitty') // kittycat
jexl: padEnd('kitty', 8, 'cat') // kittycat
jexl: replace('kittycat', 'cat', '') // kitty
jexl: replaceAll('kittycatcat', 'cat', '') // kitty
jexl: slice('kittycat', 5) // cat
jexl: substring('kittycat', 0, 5) // kitty
jexl: trim(' kitty ') // kitty, whitespace trimmed
jexl: trimStart(' kitty ') // kitty, starting whitespace trimmed
jexl: trimEnd(' kitty ') // kitty, ending whitespace trimmed
jexl: toUpperCase('kitty') // KITTY
jexl: toLowerCase('KITTY') // kitty
jexl: split('KITTY KITTY', ' ') // ['KITTY', 'KITTY']
jexl: split(feature.notThere, ' ') // [''], an absent value is read as the empty string rather than throwing
jexl: join('-', 'a', 'b', '', 'c') // a-b-c, joins truthy args with the separator
jexl: includes('kittycat', 'cat') // true
jexl: repeat('ab', 3) // ababab
jexl: jsonParse('{"a":1}') // parses a JSON string
Feature operations - getTag
jexl: getTag(feature, 'MD') // fetches MD string from BAM or CRAM feature
jexl: getTag(feature, 'HP') // fetches haplotype tag from BAM or CRAM feature
Color functions
jexl: randomColor(feature.type) // deterministic color from a string (e.g. a feature type)
jexl: alpha('green', 0.5) // a color at 50% opacity
jexl: hsl('#ff0000') // converts a color to its HSL form
jexl: colorString('green') // normalizes a color name or value to a hex string
Console logging
jexl: log(feature) // console.logs output and returns value
Binary operators
jexl: feature.flags & 2 // bitwise and to check if BAM or CRAM feature flags has 2 set
Slot defaults from plugins
jexl: logThickness(feature, 'score') // log(attribute + 1) where that is a width, else 1px; the arc display's default thickness
jexl: defaultPairedArcColor(feature, alt) // a color per SV type read off the ALT (DEL, DUP, INV, TRA, CNV)
jexl: lgvSyntenyTooltip(feature) // both sides of a synteny feature, the LGVSyntenyDisplay's default mouseover
jexl: defaultOnChordClick(feature, track, pluginManager) // opens a breakpoint split view on the clicked chord
jexl: svChordColor(feature) // the SV-type color the inspector's chords are drawn in
Variant functions
jexl: maf(feature) // minor allele frequency over the called alleles
jexl: missingness(feature) // fraction of samples with no call
jexl: impact(feature) // HIGH, MODERATE, LOW or MODIFIER, from SnpEff ANN / VEP CSQ
jexl: consequence(feature) // e.g. missense_variant, from the same annotation — the MOST SEVERE one alone
jexl: 'missense_variant' in consequences(feature) // every consequence term on the record, across all transcripts (bcftools INFO/CSQ ~ "missense_variant")
jexl: impactColor(feature) // the color the "Color by consequence impact" menu item uses
jexl: svTypeColor(feature) // the color "Color by SV type" uses
jexl: alleleLength(feature) >= 50 // longest allele in bp, so an insertion is not measured by its reference span
jexl: svType(feature) == 'DEL' // SV class, read off a symbolic ALT before falling back to INFO/SVTYPE (bcftools INFO/SVTYPE)
jexl: nAlt(feature) == 1 // ALT alleles the record declares, i.e. biallelic-only (bcftools N_ALT)
jexl: genotypeCount(feature, 'het') > 0 // samples in a genotype class — ref, alt, hom, het or mis (bcftools N_PASS(GT="het"))
The catalog is generated from the registrations themselves, core's in
packages/core/src/util/jexl.ts
and each plugin's beside the display it serves. The last two groups come from
plugins that ship with JBrowse:
- the variant functions are what the variant track's filter and color menus write, so a menu choice can be copied into a config and edited (variant tracks)
- the slot defaults are what those slots evaluate to unconfigured, listed so you can compose with one
Template strings. The jexl fork supports backtick template literals with
${...} interpolation, so a color derived from a value is
"color": "jexl:`hsl(${feature.start/100000},50%,50%)`".
Adding your own jexl function
Jexl has no variables or branchy helpers, so past a certain point an expression
stops being readable. A small plugin file with no build step adds a function to
the language, called like any built-in: "color": "jexl:customColor(feature)".
The no-build plugin tutorial is the
plugin;
customizing feature colors and
customizing feature details
call it from a color slot and a details slot.