984a3761c1300e20e463304a81098b312da7bd66
jnavarr5
Wed Nov 26 15:49:51 2025 -0800
Adding the custom track tutorial, refs #34354
diff --git src/hg/js/tutorials/customTrackTutorial.js src/hg/js/tutorials/customTrackTutorial.js
index a86d6105bbc..13610bd995b 100644
--- src/hg/js/tutorials/customTrackTutorial.js
+++ src/hg/js/tutorials/customTrackTutorial.js
@@ -1,114 +1,427 @@
/* jshint esnext: true */
/* global Shepherd */
//Creating an IIFE to prevent global variable conflicts
(function() {
const customTrackTour = new Shepherd.Tour({
defaultStepOptions: {
cancelIcon: { enabled: true },
classes: 'class-1 class-2',
scrollTo: { behavior: 'smooth', block: 'center' }
},
useModalOverlay: true,
canClickTarget: false
});
// log when a tutorial is started (commented out for now)
customTrackTour.on('start', function() {
writeToApacheLog("customTrackTutorial start " + getHgsid());
});
var tutorialButtons = {
'back': {
action() {
return this.back();
},
classes: 'shepherd-button-secondary',
text: 'Back'
},
'next': {
action() {
return this.next();
},
text: 'Next'
},
'quit': {
action() {
return this.complete();
},
classes: 'shepherd-button-secondary',
text: 'Exit'
},
+ 'load_bigWig': {
+ text: 'Load a bigWig example',
+ action () {
+ // Add text to the textarea
+ const textarea = document.querySelector('textarea[name="hgct_customText"]');
+ if (textarea.value.trim() === '') {
+ textarea.value = 'browser position chr21:33031596-33041570\n';
+ }
+ textarea.value += 'track type=bigWig name="bigWig variableStep" '+
+ 'description="bigWig variableStep format" visibility=full '+
+ 'autoScale=off viewLimits=5.0:25.0 color=255,200,0 '+
+ 'yLineMark=11.76 yLineOnOff=on '+
+ 'bigDataUrl="http://genome-test.gi.ucsc.edu/~hiram/ctDb/wigVariableStep.bw"\n';
+
+ // Optional: trigger any change events if the page listens for them
+ textarea.dispatchEvent(new Event('change', { bubbles: true }));
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
+ }
+ },
+ 'load_bigBed': {
+ text: 'Load a bigBed example',
+ action () {
+ // Add text to the textarea
+ const textarea = document.querySelector('textarea[name="hgct_customText"]');
+ if (textarea.value.trim() === '') {
+ textarea.value = 'browser position chr21:33031596-33041570\n';
+ }
+ textarea.value += 'track type=bigBed db="hg19" name="bigBed12" type="bigBed 12" '+
+ 'bigDataUrl="http://genome-test.gi.ucsc.edu/~hiram/ctDb/bigBedType12.bb "'+
+ 'visibility=pack itemRgb="On"\n';
+
+ // Optional: trigger any change events if the page listens for them
+ textarea.dispatchEvent(new Event('change', { bubbles: true }));
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
+ }
+ },
+ 'load_bed': {
+ text: 'Load a BED example',
+ action () {
+ // Add text to the textarea
+ const textarea = document.querySelector('textarea[name="hgct_customText"]');
+ if (textarea.value.trim() === '') {
+ textarea.value = 'browser position chr21:33031596-33041570\n';
+ }
+ textarea.value += 'track name="BED 6 example" priority="40" visibility=pack\n'+
+ 'chr21 33031596 33033258 item_0 0 -\n'+
+ 'chr21 33033258 33034920 item_1 100 +\n'+
+ 'chr21 33034920 33036582 item_2 200 -\n'+
+ 'chr21 33036582 33038244 item_3 300 +\n'+
+ 'chr21 33038244 33039906 item_4 400 -\n'+
+ 'chr21 33039906 33041570 item_5 500 +\n';
+
+ // Optional: trigger any change events if the page listens for them
+ textarea.dispatchEvent(new Event('change', { bubbles: true }));
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
+ }
+ },
+ 'load_wig': {
+ text: 'Load a WIG example',
+ action () {
+ // Add text to the textarea
+ const textarea = document.querySelector('textarea[name="hgct_customText"]');
+ if (textarea.value.trim() === '') {
+ textarea.value = 'browser position chr21:33031596-33041570\n';
+ }
+ textarea.value += 'track type=wiggle_0 priority="110" name="Wiggle variableStep" '+
+ 'visibility=full autoScale=off viewLimits=5.0:25.0 color=255,200,0 '+
+ 'yLineMark=11.76 yLineOnOff=on\n'+
+ 'variableStep chrom=chr21 span=1108\n'+
+ '33031597 10.0\n'+
+ '33032705 12.5\n'+
+ '33033813 15.0\n'+
+ '33034921 17.5\n'+
+ '33036029 20.0\n'+
+ '33037137 17.5\n'+
+ '33038245 15.0\n'+
+ '33039353 12.5\n'+
+ '33040461 10.0\n';
+
+ // Optional: trigger any change events if the page listens for them
+ textarea.dispatchEvent(new Event('change', { bubbles: true }));
+ textarea.dispatchEvent(new Event('input', { bubbles: true }));
+ }
+ },
+ 'submit': {
+ text: 'Submit & continue',
+ action() {
+ // Save the tour so it continues after page reload
+ sessionStorage.setItem('customTrackTourActive', 'true');
+ sessionStorage.setItem('customTrackTourStep', 'after-submit');
+ // Click the submit button
+ const submitButton = document.getElementById('Submit');
+ submitButton.click();
+ }
+ },
'end': {
action() {
hideMenu('#help > ul');
return this.complete();
},
//classes: 'shepherd-button-secondary',
text: 'Finish'
}
};
+ // Function to disable drop-downs, button clicks, and text inputs
+ function toggleSelects(containerId, enabled) {
+ const container = document.getElementById(containerId);
+
+ const selectors = ['select', 'button', 'input[type="radio"]',
+ 'input[type="submit"]', 'input[type="checkbox"]',
+ 'input[type="text"]'
+ ];
+ container.querySelectorAll(selectors).forEach(sel => {
+ if (enabled) {
+ sel.style.pointerEvents = '';
+ sel.style.opacity = '';
+ sel.tabIndex = 0;
+ } else {
+ sel.style.pointerEvents = 'none'; // blocks mouse interaction
+ sel.style.opacity = '1'; // keep visual styling normal
+ sel.tabIndex = -1; // skip from keyboard nav
+ }
+ });
+
+ }
+
+ // Function to keep menus visible using a selector
+ function keepMenuVisible(selector) {
+ const menu = document.querySelector(selector);
+ //Make sure the drop-down is visibile
+ menu.style.display = 'block';
+ menu.style.visibility = 'visible';
+ // function to keep the menu visibile
+ const makeVisible = () => {
+ menu.style.display = 'block';
+ menu.style.visibility = 'visible';
+ };
+ const events = ['mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'mousemove'];
+ // Add event listeners to keep the menu open
+ events.forEach(event => {
+ menu.addEventListener(event, makeVisible);
+ });
+ // Add event listeners to the elements of the menu list
+ menu.querySelectorAll('li').forEach(function(item) {
+ events.forEach(event => {
+ item.addEventListener(event, makeVisible);
+ });
+ });
+ }
+
+ // Function to hide the menu
+ function hideMenu(selector) {
+ const menu = document.querySelector(selector);
+ menu.style.display = 'none';
+ }
// Function to add steps to the customTrackTour
function customTrackSteps() {
customTrackTour.addStep({
title: 'Adding custom data to the Genome Browser',
text:
'In this tutorial, we '+
'will explore how to configure custom tracks '+
- 'in the various formats available for a track.
'+
+ 'in the various formats '+
+ 'available for a track.
'+
'For a stable and permanent visualization, please consider '+
- 'creating a track hub. ',
+ 'creating a track hub. If you require hosting space, '+
+ 'the UCSC Genome Browser now provides 10Gb of '+
+ 'free hosting space '+
+ 'for each user.',
buttons: [tutorialButtons.quit, tutorialButtons.next],
id: 'intro'
});
customTrackTour.addStep({
title: 'Selecting the genome assembly',
text:
'By default, your most recently viewed assembly is selected. '+
'Alter the drop-down menus to change the genome assembly.',
buttons: [tutorialButtons.back, tutorialButtons.next],
+ attachTo:
+ {
+ element: '#genome-selection-table',
+ on: 'bottom'
+ },
id: 'genome-select'
});
customTrackTour.addStep({
- title: 'Examples of custom track data Pt. 1',
+ title: 'Text-based custom tracks',
text:
- 'The simpliest way to view a custom track is to paste the '+
+ 'The simplest way to view a custom track is to paste the '+
'the track and data lines in the dialog box. '+
'A custom track consists of three items:'+
'