c8eef4a1e85bcfd8b41e87e287cbf15f09fc357a jcasper Sun Aug 16 22:19:22 2020 -0700 Replacing beforeUnload synchronous updates (blocked by Chrome) with async updates. Also fixing the suggestion box, so following a suggestion turns on the track and highlights the item. refs #25017, #25703 diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index 2f5dc77..33f1cbe 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -798,87 +798,73 @@ var updates = cart.varsToUrlData(); // clears the queue if (!url || url.length === 0) return updates; if (updates.length > 0) { var dataOnly = (url.indexOf("cgi-bin") === -1); // all urls should be to our cgis if (!dataOnly && url.lastIndexOf("?") === -1) url += "?" + updates; else url += '&' + updates; } } return url; }, - beforeUnload: function () - { // named function that can be bound and unbound to beforeunload event - // Makes sure any outstanding queued updates are sent before leaving the page. - //console.log('cart.beforeUnload: '+objKeyCount(cart.updateQueue)+' vars'); - cart.setVarsObj( {}, null, false ); // synchronous - }, - varsToUrlData: function (varsObj) { // creates a url data (var1=val1&var2=val2...) string from vars object and queue // The queue will be emptied by this call. cart.queueVarsObj(varsObj); // lay ontop of queue, to give new values precedence // Now convert to url data and clear queue var urlData = ''; if (cart.updatesWaiting()) { //console.log('cart.varsToUrlData: '+objKeyCount(cart.updateQueue)+' vars'); urlData = varHashToQueryString(cart.updateQueue); cart.updateQueue = {}; } return urlData; }, setVarsObj: function (varsObj, errFunc, async) { // Set all vars in a var hash, appending any queued updates + // The default behavior is async = true //console.log('cart.setVarsObj: were:'+objKeyCount(cart.updateQueue) + // ' new:'+objKeyCount(varsObj); cart.queueVarsObj(varsObj); // lay ontop of queue, to give new values precedence // Now ajax update all in queue and clear queue if (cart.updatesWaiting()) { setVarsFromHash(cart.updateQueue, errFunc, async); cart.updateQueue = {}; } }, setVars: function (names, values, errFunc, async) { // ajax updates the cart, and includes any queued updates. cart.updateSessionPanel(); // handles hide from left minibutton cart.setVarsObj(arysToObj(names, values), errFunc, async); }, queueVarsObj: function (varsObj) { // Add object worth of cart updates to the 'to be updated' queue, so they can be sent to // the server later. Note: hash allows overwriting previous updates to the same variable. if (typeof varsObj !== 'undefined' && objNotEmpty(varsObj)) { //console.log('cart.queueVarsObj: were:'+objKeyCount(cart.updateQueue) + // ' new:'+objKeyCount(varsObj)); for (var name in varsObj) { cart.updateQueue[name] = varsObj[name]; - - // Could update in background, however, failing to hit "refresh" is user choice - // first in queue, schedule background update - if (objKeyCount(cart.updateQueue) === 1) { - // By unbind/bind, we assure that there is only one instance bound - $(window).unbind('beforeunload', cart.beforeUnload); - $(window).bind( 'beforeunload', cart.beforeUnload); - } } } }, addVarsToQueue: function (names,values) { // creates a string of updates to save for ajax batch or a submit cart.queueVarsObj(arysToObj(names,values)); }, updateSessionPanel: function() { if (typeof recTrackSetsDetectChanges === 'undefined' || recTrackSetsDetectChanges === null) return; // change color of text @@ -924,30 +910,31 @@ { // return current visibility for given track var rec = hgTracks.trackDb[track]; if (rec) { if (rec.localVisibility) { return rec.localVisibility; } else { return vis.enumOrder[rec.visibility]; } } else { return null; } }, makeTrackVisible: function (track) { // Sets the vis box to visible, and ques a cart update, but does not update the image + // Trusts that the cart update will be submitted later. if (track && vis.get(track) !== "full") { vis.update(track, 'pack'); cart.addVarsToQueue([track], ['pack']); } }, toggleForGroup: function (button, prefix) { // toggle visibility of a track group; prefix is the prefix of all the id's of tr's in the // relevant group. This code also modifies the corresponding hidden fields and the gif // of the +/- img tag. imageV2.markAsDirtyPage(); if (arguments.length > 2) return setTableRowVisibility(button, prefix, "hgtgroup", "group",false,arguments[2]); else return setTableRowVisibility(button, prefix, "hgtgroup", "group",false); @@ -970,31 +957,31 @@ var sels = $('select.normalText,select.hiddenText'); $(sels).change(function() { var track = $(this).attr('name'); if ($(this).val() === 'hide') { var rec = hgTracks.trackDb[track]; if (rec) rec.visibility = 0; // else Would be nice to hide subtracks as well but that may be overkill $(document.getElementById('tr_' + track)).remove(); cart.updateSessionPanel(); imageV2.highlightRegion(); $(this).attr('class', 'hiddenText'); } else $(this).attr('class', 'normalText'); - cart.addVarsToQueue([track], [$(this).val()]); + cart.setVars([track], [$(this).val()]); imageV2.markAsDirtyPage(); return false; }); // Now we can rid the submt of the burden of all those vis boxes var form = $('form#TrackForm'); $(form).submit(function () { $('select.normalText,select.hiddenText').attr('disabled',true); }); $(form).attr('method','get'); }, restoreFromBackButton: function() // Re-enabling vis dropdowns is necessary because initForAjax() disables them on submit. { $('select.normalText,select.hiddenText').attr('disabled',false); @@ -2314,31 +2301,32 @@ success: catchErrorOrDispatch, error: errorHandler, cmd: cmd, loadingId: showLoadingImage("imgTbl"), cache: false }); } else { // do a full page refresh to update hgTracks image jQuery('body').css('cursor', 'wait'); var ele; if (document.TrackForm) ele = document.TrackForm; else ele = document.TrackHeaderForm; if (name) - cart.addVarsToQueue(['hgFind.matches'], [name]); + // Add or update form input with gene to highlight + suggestBox.updateFindMatches(name); ele.submit(); } } } } else if (cmd === 'zoomCodon' || cmd === 'zoomExon') { var num, ajaxCmd, msg; if (cmd === 'zoomCodon') { msg = "Please enter the codon number to jump to:"; ajaxCmd = 'codonToPos'; } else { msg = "Please enter the exon number to jump to:"; ajaxCmd = 'exonToPos'; } rightClick.myPrompt(msg, function(results) { $.ajax({ @@ -2581,31 +2569,31 @@ } else if (tdbIsFolderContent(rec)) { // supertrack children need to have _sel set to trigger superttrack reshaping cart.setVars( [ id, id+"_sel" ], [ 'hide', 0 ] ); } else { cart.setVars([id], ['hide']); // Others, just set vis hide. } $(document.getElementById('tr_' + id)).remove(); imageV2.afterImgChange(true); } else if (!imageV2.mapIsUpdateable) { jQuery('body').css('cursor', 'wait'); if (selectUpdated) { // assert(document.TrackForm); document.TrackForm.submit(); } else { // Add vis update to queue then submit - cart.addVarsToQueue([id], [cmd]); + cart.setVars([id], [cmd], null, false); // synchronous document.TrackHeaderForm.submit(); } } else { imageV2.requestImgUpdate(id, id + "=" + cmd, "", cmd); } } }, makeHitCallback: function (title) { // stub to avoid problem with a function closure w/n a loop return function(menuItemClicked, menuObject) { rightClick.hit(menuItemClicked, menuObject, title); return true; }; }, @@ -3696,43 +3684,43 @@ reg = new RegExp("([\\S\\s]+?)"); a = reg.exec(response); if (a && a[1]) { $('.trackTiming').replaceWith(a[1]); } }, loadSuggestBox: function () { if ($('#positionInput').length) { if (!suggestBox.initialized) { // only call init once suggestBox.init(getDb(), $("#suggestTrack").length > 0, function (item) { genomePos.set(item.id, getSizeFromCoordinates(item.id)); + if ($("#suggestTrack").length && $('#hgFindMatches').length) { + // Set cart variables to open the hgSuggest gene track and highlight + // the chosen transcript. These variables will be submittted by the goButton + // click handler. + vis.makeTrackVisible($("#suggestTrack").val()); + cart.addVarsToQueue(["hgFind.matches"],[$('#hgFindMatches').val()]); + } $("#goButton").click(); }, function (position) { genomePos.set(position, getSizeFromCoordinates(position)); - }); } - // Make sure suggestTrack is visible when user chooses via gene select (#3484). - if ($("#suggestTrack").length) { - $(document.TrackForm || document.TrackHeaderForm).submit(function(event) { - if ($('#hgFindMatches').length) { - vis.makeTrackVisible($("#suggestTrack").val()); - } - }); + ); } } }, afterImgChange: function (dirty) { // Standard things to do when manipulations change image without ajax update. dragReorder.init(); dragSelect.load(false); imageV2.highlightRegion(); if (dirty) imageV2.markAsDirtyPage(); }, afterReload: function (id) { // Reload various UI widgets after updating imgTbl map. @@ -4181,34 +4169,30 @@ navigateInPlace: function (params, disabledEle, keepCurrentTrackVisible) { // request an hgTracks image, using params // disabledEle is optional; this element will be enabled when update is complete // If keepCurrentTrackVisible is true, we try to maintain relative position of the item // under the mouse after the in-place update. // Tim thinks we should consider disabling all UI input while we are doing in-place update. // TODO: waitOnFuction? // No ajax image update if there are too many tracks! if (imageV2.manyTracks()) { imageV2.fullReload(params); return false; // Shouldn't return from fullReload but I have seen it in FF } - // If UCSC Genes (or any suggestion) is supposed to be made visible, then do so - if ($("#suggestTrack").length && $('#hgFindMatches').length) - vis.makeTrackVisible($("#suggestTrack").val()); - jQuery('body').css('cursor', 'wait'); var currentId, currentIdYOffset; if (keepCurrentTrackVisible) { var item = rightClick.currentMapItem || imageV2.lastTrack; if (item) { var top = $(document.getElementById("tr_" + item.id)).position().top; if (top >= $(window).scrollTop() || top < $(window).scrollTop() + $(window).height()) { // don't bother if the item is not currently visible. currentId = item.id; currentIdYOffset = top - $(window).scrollTop(); } } } $.ajax({