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" (see also Footnote 1)

// 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, and you can call feature.get('start'), feature.get('end'), feature.get('refName'), or feature.get('other_attribute') for e.g. maybe a field in a GFF3 column 9

Footnote 0. See our no-build plugin tutorial for more info on setting up a simple plugin for doing these customizations.

Footnote 1. myplugin.js does not have to use the jbrowse-plugin-template if it is small and self contained like this, and does not import other modules. if you import other modules from your plugin, then it can be worth it to use the jbrowse-plugin-template.

Footnote 2. if you are using embedded, there are also other methods of including plugins, see https://jbrowse.org/storybook/lgv/main/?path=/story/using-plugins--page