Skip to main content

Using jexl callbacks

We use Jexl for defining configuration callbacks.

An example of a Jexl configuration callback might look like this:

"color": "jexl:get(feature,'strand')==-1?'red':'blue'"

The notation get(feature,'strand') is the same as feature.get('strand') in javascript code.

We have a number of functions available in jexl, such as:

Feature operations - get

jexl: get(feature, 'start') // start coordinate, 0-based half open
jexl: get(feature, 'end') // end coordinate, 0-based half open
jexl: get(feature, 'refName') // chromosome or reference sequence name
jexl: get(feature, 'CIGAR') // BAM or CRAM feature CIGAR string
jexl: get(feature, 'seq') // BAM or CRAM feature sequence
jexl: get(feature, 'type') // feature type e.g. mRNA or gene

Feature operations - getTag

The getTag function smooths over slight differences in BAM and CRAM features to access their tags

jexl: getTag(feature, 'MD') // fetches MD string from BAM or CRAM feature
jexl: getTag(feature, 'HP') // fetches haplotype tag from BAM or CRAM feature

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']

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')

Console logging

jexl: log(feature) // console.logs output and returns value
jexl: cast({ mRNA: 'green', pseudogene: 'purple' })[get(feature, 'type')] // returns either green or purple depending on feature type

Binary operators

jexl: get(feature, 'flags') & 2 // bitwise and to check if BAM or CRAM feature flags has 2 set

Making sophisticated color callbacks

If you have a color callback that has a lot of logic in it, then using jexl to express all that logic may be hard. Instead, you can make a small plugin which adds a function to the jexl language, and call that function in your jexl callback.

For example, create a file named "myplugin.js":

note

The example below uses the IIFE/umdLoc format. If you are using esmLoc, use export default class MyPlugin instead (see customizing feature colors for that pattern). myplugin.js does not need the jbrowse-plugin-template as long as it is self-contained and does not import other modules.

// myplugin.js
;(function () {
class MyPlugin {
install() {}
configure(pluginManager) {
pluginManager.jexl.addFunction('colorFeature', feature => {
let type = feature.get('type')
if (type === 'CDS') {
return 'red'
} else if (type === 'exon') {
return 'green'
} else {
return 'purple'
}
})
}
}

// the plugin will be included in both the main thread and web worker, so
// install plugin to either window or self (webworker global scope)
;(typeof self !== 'undefined' ? self : window).JBrowsePluginMyPlugin = {
default: MyPlugin,
}
})()

Then put myplugin.js in the same folder as your config file, and then you can use the custom jexl function in your config callbacks as follows:

{
"plugins": [
{
"name": "MyPlugin",
"umdLoc": { "uri": "myplugin.js" }
}
],
"tracks": [
{
"type": "FeatureTrack",
"trackId": "my_track",
"name": "my track",
"assemblyNames": ["hg19"],
"adapter": {
"type": "Gff3TabixAdapter",
"gffLocation": {
"uri": "volvox.filtered.gff"
}
},
"displays": [
{
"type": "LinearBasicDisplay",
"displayId": "mytrack-LinearBasicDisplay",
"renderer": {
"type": "SvgFeatureRenderer",
"color1": "jexl:colorFeature(feature)"
}
}
]
}
]
}

The feature in the callback is a "SimpleFeature" type object; you can call feature.get('start'), feature.get('end'), feature.get('refName'), or feature.get('other_attribute') for e.g. a field in a GFF3 column 9.

See the no-build plugin tutorial for a full walkthrough of setting up a plugin like this.