Skip to content

Commit

Permalink
Add "recently loaded" genome sublist
Browse files Browse the repository at this point in the history
  • Loading branch information
jrobinso committed Feb 6, 2024
1 parent d03c667 commit da0f554
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 82 deletions.
25 changes: 16 additions & 9 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
*/

import igv from '../node_modules/igv/dist/igv.esm.js'
import igv from '../node_modules/igv/js/index.js'
import * as GoogleAuth from '../node_modules/google-utils/src/googleAuth.js'
import * as GooglePicker from '../node_modules/google-utils/src/googleFilePicker.js'
import {makeDraggable} from "./draggable.js"
Expand Down Expand Up @@ -96,16 +96,22 @@ async function main(container, config) {
config.igvConfig.genomes = config.genomes
}

const igvConfig = config.igvConfig
// Custom (user loaded) genomes
let recentGenomes
const customGenomeString = localStorage.getItem("recentGenomes")
if (customGenomeString) {
recentGenomes = JSON.parse(customGenomeString)
}

const igvConfigGenome = igvConfig.genome
const igvConfig = config.igvConfig
const igvConfigGenome = igvConfig.genome // the genome specified in the configuration file
if (config.restoreLastGenome) {
try {
const lastGenomeId = localStorage.getItem("genomeID")
if (lastGenomeId && lastGenomeId !== igvConfig.genome) {
if (config.genomes.find(elem => elem.id === lastGenomeId) ||
((lastGenomeId.startsWith("GCA_") || lastGenomeId.startsWith("GCF_")) && lastGenomeId.length >= 13))
{
if (config.genomes && config.genomes.find(elem => elem.id === lastGenomeId) ||
(recentGenomes && recentGenomes.find(elem => elem.id === lastGenomeId)) ||
((lastGenomeId.startsWith("GCA_") || lastGenomeId.startsWith("GCF_")) && lastGenomeId.length >= 13)) {
igvConfig.genome = lastGenomeId
igvConfig.tracks = []
}
Expand Down Expand Up @@ -176,12 +182,12 @@ async function main(container, config) {
}
}

// TODO -- fix this hack. We are assuming th eerror is due to the "last genome loaded, it could be anything.
// TODO -- fix this hack. We are assuming th error is due to the "last genome loaded, it could be anything.
let browser
try {
browser = await igv.createBrowser(container, igvConfig)
browser = await igv.createBrowser(container, igvConfig)
} catch (e) {
if(igvConfigGenome !== igvConfig.genome) {
if (igvConfigGenome !== igvConfig.genome) {
igv.removeAllBrowsers()
igvConfig.genome = igvConfigGenome
browser = await igv.createBrowser(container, igvConfig)
Expand Down Expand Up @@ -240,6 +246,7 @@ async function initializationHelper(browser, container, options) {

}

// Create widgets for URL and File loads.
createGenomeWidgets({
$igvMain,
urlModalId: 'igv-app-genome-from-url-modal',
Expand Down
153 changes: 81 additions & 72 deletions js/genomeWidgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,41 +24,48 @@
* THE SOFTWARE.
*/

import {AlertSingleton, createURLModal,EventBus,FileLoadManager,FileLoadWidget,Utils} from '../node_modules/igv-widgets/dist/igv-widgets.js'
import Globals from "./globals.js";
import {
AlertSingleton,
createURLModal,
FileLoadManager,
FileLoadWidget,
Utils
} from '../node_modules/igv-widgets/dist/igv-widgets.js'
import Globals from "./globals.js"

const MAX_CUSTOM_GENOMES = 5
let fileLoadWidget;
let knownGenomeIds = new Set();

let knownGenomeIds

function createGenomeWidgets({$igvMain, urlModalId, genomeFileLoad}) {

// URL modal
const $urlModal = $(createURLModal(urlModalId, 'Genome URL'))
$igvMain.append($urlModal);
$igvMain.append($urlModal)

let config =
{
widgetParent: $urlModal.find('.modal-body').get(0),
dataTitle: 'Genome',
indexTitle: 'Index',
mode: 'url',
fileLoadManager: new FileLoadManager(),
dataOnly: false,
doURL: true
};

fileLoadWidget = new FileLoadWidget(config);
// File widget
const fileLoadWidget = new FileLoadWidget({
widgetParent: $urlModal.find('.modal-body').get(0),
dataTitle: 'Genome',
indexTitle: 'Index',
mode: 'url',
fileLoadManager: new FileLoadManager(),
dataOnly: false,
doURL: true
})

// Configures both file widget and url modal, a bit confusing
Utils.configureModal(fileLoadWidget, $urlModal.get(0), async fileLoadWidget => {

try {
await genomeFileLoad.loadPaths( fileLoadWidget.retrievePaths() )
await genomeFileLoad.loadPaths(fileLoadWidget.retrievePaths())
} catch (e) {
console.error(e);
console.error(e)
AlertSingleton.present(e)
}

});
})
}

/**
Expand All @@ -74,14 +81,14 @@ async function initializeGenomeWidgets(browser, genomes, $dropdown_menu) {
try {

// Start with predefined genomes. This can return undefined.
let genomeMap = await getAppLaunchGenomes(genomes);
knownGenomeIds = genomes ? new Set(genomes.map(g => g.id)) : new Set()
let genomeList = await getAppLaunchGenomes(genomes)

// Add user loaded genomes
genomeMap = addCustomGenomes(genomeMap || new Map())
genomeList = addCustomGenomes(genomeList || [])

if(genomeMap.size > 0) {
genomeDropdownLayout({browser, genomeMap, $dropdown_menu});
knownGenomeIds = new Set(genomeMap.keys())
if (genomeList.length > 0) {
updateGenomeList({browser, genomeList, $dropdown_menu})
}

} catch (e) {
Expand All @@ -92,39 +99,38 @@ async function initializeGenomeWidgets(browser, genomes, $dropdown_menu) {
async function getAppLaunchGenomes(genomes) {

if (undefined === genomes) {
return undefined;
return undefined
}
if (Array.isArray(genomes)) {
return buildMap(genomes);
return genomes
} else {

let response = undefined;
let response = undefined
try {
response = await fetch(genomes);
response = await fetch(genomes)
} catch (e) {
AlertSingleton.present(e.message);
AlertSingleton.present(e.message)
}

if (response) {
let json = await response.json();
return buildMap(json);
let json = await response.json()
return json
}

}
}

function addCustomGenomes(genomeMap) {
const customGenomeString = localStorage.getItem("customGenomes")
if(customGenomeString) {
function addCustomGenomes(genomeList) {
const customGenomeString = localStorage.getItem("recentGenomes")
if (customGenomeString) {
const customGenomeJson = JSON.parse(customGenomeString)
if(customGenomeJson.length > 0) {
genomeMap.set('-', '-')
if (customGenomeJson.length > 0) {
genomeList.push('-')
for (let json of customGenomeJson.reverse()) {
genomeMap.set(json.id, json)
genomeList.push(json)
}
}
}
return genomeMap
return genomeList
}

function buildMap(arrayOrJson) {
Expand All @@ -144,81 +150,84 @@ function buildMap(arrayOrJson) {
return map
}

function genomeDropdownLayout({browser, genomeMap, $dropdown_menu}) {
function updateGenomeList({browser, genomeList, $dropdown_menu}) {

// discard all buttons preceeding the divider div
// TODO -- does this use of find assume there is 1 dropdown-divider? Searching by ID would be more robust.
let $divider = $dropdown_menu.find('.dropdown-divider');
$divider.nextAll().remove();
let $divider = $dropdown_menu.find('.dropdown-divider')
$divider.nextAll().remove()

for (let [ key, value ] of genomeMap) {
for (let genomeJson of genomeList) {

if('-' === key) {
// Add separator
// TODO -- style this, <hr> is a hack
$('<div class="dropdown-divider"></div>').insertAfter($divider)
const key = genomeJson.id
const value = genomeJson

if ('-' === genomeJson) {
$('<div class="dropdown-divider"></div>').insertAfter($divider)
} else {
const $button = createButton(value.name);
$button.insertAfter($divider);
const $button = createButton(value.name)
$button.insertAfter($divider)

$button.data('id', key);
$button.data('id', key)

const str = `click.genome-dropdown.${key}`;
const str = `click.genome-dropdown.${key}`

$button.on(str, async () => {

const id = $button.data('id');
const id = $button.data('id')

if (id !== browser.genome.id) {
await loadGenome(value);
await loadGenome(value)
}

});
})
}

} // for (...)

function createButton(title) {

let $button = $('<button>', {class: 'dropdown-item', type: 'button'});
$button.text(title);
let $button = $('<button>', {class: 'dropdown-item', type: 'button'})
$button.text(title)

return $button;
return $button
}

}

async function loadGenome(genomeConfiguration) {
async function loadGenome(genomeConfiguration, custom = false) {

let g = undefined;
let g = undefined
try {
g = await Globals.browser.loadGenome(genomeConfiguration);
if(g.id) {
g = await Globals.browser.loadGenome(genomeConfiguration)
if (g.id) {
try {

// Last loaded genome ID, reloaded automatically on next page load
localStorage.setItem("genomeID", g.id)

// If this is a previously unknown genome add it to the custom list.
if(!knownGenomeIds.has(g.id)) {
knownGenomeIds.add(g.id)
const customGenomeString = localStorage.getItem("customGenomes")
let customGenomeJson = customGenomeString ? JSON.parse(customGenomeString) : []
customGenomeJson.unshift(g)
if(customGenomeJson.length > MAX_CUSTOM_GENOMES) {
customGenomeJson = customGenomeJson.slice(0,MAX_CUSTOM_GENOMES)
}
localStorage.setItem("customGenomes", JSON.stringify(customGenomeJson))
// Update the recently loaded list
// hub.txt genomes are indirect, record name and id
if (!genomeConfiguration.id) genomeConfiguration.id = g.id
if (!genomeConfiguration.name) genomeConfiguration.name = g.name

const recentGenomesString = localStorage.getItem("recentGenomes")
let recentGenomes = recentGenomesString ? JSON.parse(recentGenomesString) : []
recentGenomes = recentGenomes.filter(r => r.id !== g.id) // If already present, replace
recentGenomes.unshift(genomeConfiguration)
if (recentGenomes.length > MAX_CUSTOM_GENOMES) {
recentGenomes = recentGenomes.slice(0, MAX_CUSTOM_GENOMES)
}
localStorage.setItem("recentGenomes", JSON.stringify(recentGenomes))


} catch (e) {
console.error(e)
}
}

} catch (e) {
console.error(e);
console.error(e)
AlertSingleton.present(e)
}

Expand Down
2 changes: 1 addition & 1 deletion js/shareWidgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
*/

import igv from '../node_modules/igv/dist/igv.esm.js'
import igv from '../node_modules/igv/js/index.js'
import {AlertSingleton, QRCode} from '../node_modules/igv-widgets/dist/igv-widgets.js'
import {setURLShortener, shortSessionURL} from './shareHelper.js'

Expand Down

0 comments on commit da0f554

Please sign in to comment.