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.<br><br>'+
+                  'in the <a href="/FAQ/FAQformat.html" target="_blank">various formats</a> '+
+                  'available for a track.<br><br>'+
                   'For a stable and permanent visualization, please consider '+
-                  'creating a track hub. ',
+                  'creating a <a href="/goldenPath/help/hgTrackHubHelp.html" '+
+                  'target="blank">track hub</a>. If you require hosting space, '+
+                  'the UCSC Genome Browser now provides 10Gb of '+
+                  '<a href="hgHubConnect?#hubUpload" target="_blank">free hosting space</a> '+
+                  '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:'+
                   '<ol>'+
-                  '    <li>browser lines (optional)</li>'+
-                  '    <li>track line</li>'+
+                  '    <li><a href="/goldenPath/help/hgTracksHelp.html#BROWSER" '+
+                  '        target="_blank">browser line</a>: (optional)<br>'+
+                  '        Control aspects of the overall '+
+                  '        display of the Genome Browser</li>'+
+                  '    <li><a href="/goldenPath/help/hgTracksHelp.html#TRACK" '+
+                  '        target="_blank">track line</a>:<br> '+
+                  '        Defines the display attributes for '+
+                  '        all lines in the annotation data set</li>'+
                   '    <li>data lines</li>'+
                   '</ol>'+
                   'Alternatively, you can store the custom track data in a file on a web server, '+
                   'and paste the URL to the file to load the custom track. ',
-            buttons: [tutorialButtons.back, tutorialButtons.next],
+            buttons: [tutorialButtons.back, tutorialButtons.load_bed,
+                      tutorialButtons.load_wig, tutorialButtons.next],
+            attachTo: 
+                {
+                element: '#data-input',
+                on: 'right'
+                },
             id: 'dialog-box'
         });
         customTrackTour.addStep({
-            title: 'Examples of custom track data Pt. 2', 
+            title: 'Binary-indexed custom tracks',
             text:
-                  'You can use the <em>bigDataUrl</em> parameter in the track line ' +
-                  'to upload a bigBed, bigWig, or other binary indexed file',
-            buttons: [tutorialButtons.back, tutorialButtons.next],
+                  'To view a bigBed, bigWig, or another binary-indexed file, the file must be '+
+                  '<a href="/goldenPath/help/hgTrackHubHelp.html#Hosting" target="_blank">hosted '+
+                  'on an external server</a> that allows byte-range requests. '+
+                  'Binary-indexed custom tracks only need a <em>track</em> '+
+                  'line to define the custom track as it uses a '+
+                  '<a href="/goldenPath/help/trackDb/trackDbHub.html#bigDataUrl" '+
+                  'target="_blank"><em>bigDataUrl</em></a> setting to fetch the annotations.'+
+                  '<br><br>'+
+                  'Alternatively, you can paste the URL directly without a "track line" to '+
+                  'quickly view the file. The benefit in using a track line with a '+
+                  '<em>bigDataUrl</em> parameter is ' +
+                  'to add other configuration options for the big* file.',
+            buttons: [tutorialButtons.back, tutorialButtons.load_bigBed,
+                      tutorialButtons.load_bigWig, tutorialButtons.next],
+            attachTo: 
+                {
+                element: '#data-input',
+                on: 'right'
+                },
             id: 'bigCustom-tracks'
         });
         customTrackTour.addStep({
             title: '(Optional) Upload track documentation',
             text:
-                  ''+
-                  '',
+                  'In this dialoge box, you can add HTML that will be shown on the custom '+
+                  'track\'s description page. '+
+                  '<br><br>'+
+                  'An <a href="/goldenPath/help/ct_description.txt" '+
+                  'target="_blank">example HTML</a> text is provided, and can '+
+                  'be edited to fit your needs. ',
             buttons: [tutorialButtons.back, tutorialButtons.next],
+            attachTo: 
+                {
+                element: '#description-input',
+                on: 'right'
+                },
             id: 'documentation-box'
         });
     
+        customTrackTour.addStep({
+            title: 'Submit the custom track',
+            text:
+                  'Once you paste/upload your custom track data, you can use the '+
+                  '<em><b>Submit</b></em> button to send the data to the UCSC Genome Browser. ',
+            buttons: [tutorialButtons.back, tutorialButtons.submit],
+            when: {
+                show: function() {
+                    const textarea = document.querySelector('textarea[name="hgct_customText"]');
+                    const isEmpty = textarea.value.trim() === '';
+                    
+                    // If the textbox is empty, load a BED track
+                    if (isEmpty) {
+                        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 }));
+                    }
+                }
+            },
+            attachTo: 
+                {
+                element: '#Submit',
+                on: 'top'
+                },
+            id: 'submit'
+        });
+    }
+
+    function customTrackResultSteps() {
+        customTrackTour.addStep({
+            title: 'View your uploaded custom tracks',
+            text:
+                  'This table shows the custom tracks that you uploaded to the UCSC Genome Browser '+
+                  'from the previous page. Using this table, you can delete any unwanted custom '+
+                  'tracks. ',
+            buttons: [tutorialButtons.next],
+            attachTo: 
+                {
+                element: '#resultsTable',
+                on: 'bottom'
+                },
+            id: 'after-submit'
+        });
+
+        customTrackTour.addStep({
+            title: '"View in" drop-down menu',
+            text:
+                  'By default, the Genome Browser will take you to view your custom track '+
+                  'on the main Genome Browser image. <br><br>'+
+                  'Altering this drop-down will let you send '+
+                  'the data to the Table Browser, Variant Annotation Integrator, or the '+
+                  'Data Integrator.',
+            buttons: [tutorialButtons.back, tutorialButtons.next],
+            attachTo: 
+                {
+                element: '#navSelect',
+                on: 'bottom'
+                },
+            id: 'navigation-drop-down'
+        });
+
+        customTrackTour.addStep({
+            title: 'Add more tracks to the Genome Browser',
+            text:
+                  'Click this button to return to the previous page to add more custom tracks.',
+            buttons: [tutorialButtons.back, tutorialButtons.next],
+            attachTo: 
+                {
+                element: '#addTracksButton',
+                on: 'bottom'
+                },
+            id: 'add-tracks'
+        });
+
+        customTrackTour.addStep({
+            title: 'Additional resources and documentation',
+            text: 'For further examples of using the ' +
+                  'Table Browser, please read the <a href="/goldenPath/help/hgTablesHelp.html" '+
+                  'target="_blank">Table Browser user guide</a>. You can find examples of batch '+
+                  'queries, filtering on fields from tables, video examples, and more. '+
+                  '<br><br>'+
+                  'You can also <a href="/contacts.html" target="_blank">contact us</a> if you '+
+                  'have any questions or issues using a dataset '+
+                  'on the Table Browser.',
+            attachTo: {
+                element: '#hgCustomHelp',
+                on: 'left-start'
+            },
+            buttons: [ tutorialButtons.back, tutorialButtons.end ],
+            when: {
+                show: () => {
+                    keepMenuVisible('#help > ul');
+                },
+                hide: () => hideMenu('#help > ul')
+            },
+            id: 'additional-help'
+        });
+    
     }
 
     if (typeof window.customTrackTour === 'undefined') {
+        // Unique ID to verify that you are on the results page
         customTrackSteps();
         //Export the customTrackTour globalally
         window.customTrackTour = customTrackTour;
     }
+    // Condition to continue the tutorial once you submit the custom track
+    if (sessionStorage.getItem('customTrackTourActive') === 'true') {
+        const stepToShow = sessionStorage.getItem('customTrackTourStep');
+        
+        // Clear the session storage
+        sessionStorage.removeItem('customTrackTourActive');
+        sessionStorage.removeItem('customTrackTourStep');
+        
+        // Resume tour at the saved step
+        if (stepToShow) {
+            customTrackResultSteps();
+            customTrackTour.show(stepToShow);
+        }
+    }
 })();