564e19b4db75608adf7d6273907a23ec553f42ca angie Mon Apr 30 17:23:51 2012 -0700 hgVarAnnogrator now stores querySpec in the cart, and uses the cart'squerySpec (if exists) to draw the page. It also does a reload when the user hits the back button, because the web browser forgets all AJAX updates and the cart has the latest state. It does synchronous AJAX to lookup the position before executing a query; that causes failure of ajax.js's setCartVars async GET, so in order to avoid a warning about network error, we ignore errors for setCartVars. (#6152) diff --git src/hg/js/ajax.js src/hg/js/ajax.js index 9a0d868..a3b24c9 100644 --- src/hg/js/ajax.js +++ src/hg/js/ajax.js @@ -1,457 +1,460 @@ // AJAX Utilities var debug = false; var req; function nullProcessReqChange() { if(debug && this.readyState == 4) alert("req.responseText: " + req.responseText); } // The ajaxWaitCount code is currently NOT used, but we are keeping it in case in the future we decide we // really need to support sequential AJAX calls (without just using synchronous AJAX calls). // // When setting vars with ajax, it may be necessary to wait for response before newer calls // Therefore this counter is set up to allow an "outside callback" when the ajax is done // The typical scenario is setCartVar by ajax, followed immmediately by a form submit. // To avoid the race condition, the form gets submitted after the ajax returns var ajaxWaitCount = 0; function ajaxWaitIsDone() { return ( ajaxWaitCount <= 0 ); } function ajaxWaitCountUp() { ajaxWaitCount++; } function ajaxWaitCountReset() { ajaxWaitCount = 0; } //function ajaxWaitCountShow() { warn("ajaxWait calls outstanding:"+ajaxWaitCount); } // Here is where the "outside callback" gets set up and called var ajaxWaitCallbackFunction = null; var ajaxWaitCallbackTimeOut = null; function ajaxWaitCallbackRegister(func) { // register a function to be called when the ajax waiting is done. if(ajaxWaitIsDone()) func(); else { ajaxWaitCallbackFunction = func; ajaxWaitCallbackTimeOut = setTimeout("ajaxWaitCallback();",5000); // just in case } } function ajaxWaitCallback() { // Perform the actual function (which could have been because of a callback or a timeout) // Clear the timeout if this is not due to ajaxWaitDone //warn("ajaxWaitCallback: "+ajaxWaitIsDone()); if(ajaxWaitCallbackTimeOut != null) { clearTimeout(ajaxWaitCallbackTimeOut); ajaxWaitCallbackTimeOut = null; } // Clear the wait stack incase something failed and we are called by a timeout ajaxWaitCountReset(); // Finally do the function if(ajaxWaitCallbackFunction && jQuery.isFunction(ajaxWaitCallbackFunction)) { ajaxWaitCallbackFunction(); } ajaxWaitCallbackFunction = null; } function ajaxWaitCountDown() { // called whenever an ajax request is done if((req && req.readyState == 4) || (this.readyState == 4)) { // It is only state 4 that means done ajaxWaitCount--; if(ajaxWaitIsDone()) ajaxWaitCallback(); } //warn(req.readyState + " waiters:"+ajaxWaitCount); } // UNUSED but useful ? // var formToSubmit = null; // multistate: null, {form}, "COMPLETE", "ONCEONLY" // function formSubmit() // { // This will be called as a callback on timeout or ajaxWaitCallback // // if(formToSubmit != null) { // //warn("submitting form:"+$(formToSubmit).attr('name') + ": "+ajaxWaitIsDone()); // var form = formToSubmit; // formToSubmit = "GO"; // Flag to wait no longer // $(form).submit(); // } // waitMaskClear(); // clear any outstanding waitMask. overkill if the form has just been submitted // } // function formSubmitRegister(form) // { // Registers the form submit to be done by ajaxWaitCallback or timeout // if(formToSubmit != null) // Repeated submission got through, so ignore it // return false; // waitMaskSetup(5000); // Will prevent repeated submissions, I hope. // formToSubmit = form; // //warn("Registering form to submit:"+$(form).attr('name')); // ajaxWaitCallbackRegister(formSubmit); // return false; // Don't submit until ajax is done. // } // // function formSubmitWaiter(e) // { // Here we will wait for up to 5 seconds before continuing. // if(formToSubmit == null) // return formSubmitRegister(e.target); // register on first time through // // if(formToSubmit == "GO") { // Called again as complete // //warn("formSubmitWaiter(): GO"); // formToSubmit = "STOP"; // Do this only once! // return true; // } // return false; // } // // function formSubmitWaitOnAjax(form) // { // Most typically, we block a form submit until all ajax has returned // $(form).unbind('submit', formSubmitWaiter ); // prevents multiple bind requests // $(form).bind( 'submit', formSubmitWaiter ); // } function loadXMLDoc(url) { // Load XML without a request handler; this is useful if you are sending one-way messages. loadXMLDoc(url, null); } function loadXMLDoc(url, callBack) { // From http://developer.apple.com/internet/webcontent/xmlhttpreq.html //warn("AJAX started: "+url); if(callBack == null) callBack = nullProcessReqChange; req = false; // branch for native XMLHttpRequest object if(window.XMLHttpRequest && !(window.ActiveXObject)) { try { req = new XMLHttpRequest(); } catch(e) { req = false; } } else if(window.ActiveXObject) { // branch for IE/Windows ActiveX version try { req = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) { try { req = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { req = false; } } } if(debug) alert(url); if(req) { req.onreadystatechange = callBack; req.open("GET", url, true); req.send(); //req.send(""); } } -function setCartVars(names, values) +function setCartVars(names, values, errFunc) { // Asynchronously sets the array of cart vars with values if(names.length <= 0) return; + if (errFunc == null) + errFunc = errorHandler; + // Set up constant portion of url var loc = window.location.href; if(loc.indexOf("?") > -1) { loc = loc.substring(0, loc.indexOf("?")); } if(loc.lastIndexOf("/") > -1) { loc = loc.substring(0, loc.lastIndexOf("/")); } loc = loc + "/cartDump"; var hgsid = getHgsid(); var data = "submit=1&noDisplay=1&hgsid=" + hgsid; var track = getTrack(); if(track && track.length > 0) data = data + "&g=" + track; for(var ix=0; ix<names.length; ix++) { data = data + "&" + encodeURIComponent(names[ix]) + "=" + encodeURIComponent(values[ix]); } var type; // We prefer GETs so we can analyze logs, but use POSTs if data is longer than a (conservatively small) // maximum length to avoid problems on older versions of IE. if((loc.length + data.length) > 2000) { type = "POST"; } else { type = "GET"; } $.ajax({ type: type, url: loc, data: data, trueSuccess: function () {}, success: catchErrorOrDispatch, - error: errorHandler, + error: errFunc, cache: false }); } function setCartVar(name, value) { // Asynchronously set a cart variable. setCartVars( [ name ], [ value ] ); } function setVarsFromHash(varHash) { // Set all vars in a var hash // If obj is undefined then obj is document! var names = []; var values = []; for (var aVar in varHash) { names.push(aVar); values.push(varHash[aVar]); } if(names.length > 0) { setCartVars(names,values); } } function setAllVars(obj,subtrackName) { // Set all enabled inputs and selects found as children obj with names to cart with ajax // If obj is undefined then obj is document! var names = []; var values = []; if($(obj) == undefined) obj = $('document'); setVarsFromHash(getAllVars(obj,subtrackName)); } // Unused but useful // function setCartVarFromObjId(obj) // { // setCartVar($(obj).attr('id'),$(obj).val()); // } function submitMain() { $('form[name="mainForm"]').submit(); } function setCartVarAndRefresh(name,val) { setCartVar(name,val); var main=$('form[name="mainForm"]') $(main).attr('action',window.location.href); setTimeout("submitMain()",50); // Delay in submit helps ensure that cart var has gotten there first. return false; } function errorHandler(request, textStatus) { var str; var tryAgain = true; if(textStatus && textStatus.length && textStatus != "error") { str = "Encountered network error : '" + textStatus + "'."; } else { if(request.responseText) { tryAgain = false; str = "Encountered error: '" + request.responseText + "'"; } else { str = "Encountered a network error." } } if(tryAgain) str += " Please try again. If the problem persists, please check your network connection."; warn(str); jQuery('body').css('cursor', ''); if(this.disabledEle) { this.disabledEle.removeAttr('disabled'); } if(this.loadingId) { hideLoadingImage(this.loadingId); } } function catchErrorOrDispatch(obj, textStatus) { // generic ajax success handler (handles fact that success is not always success). if(textStatus == 'success') this.trueSuccess(obj, textStatus); else errorHandler.call(this, obj, textStatus); } // Specific calls... function lookupMetadata(trackName,showLonglabel,showShortLabel) { // Ajax call to repopulate a metadata vals select when mdb var changes var thisData = "db=" + getDb() + "&cmd=tableMetadata&track=" + trackName; if(showLonglabel) thisData += "&showLonglabel=1"; if(showShortLabel) thisData += "&showShortLabel=1"; $.ajax({ type: "GET", dataType: "html", url: "../cgi-bin/hgApi", data: thisData, trueSuccess: loadMetadataTable, success: catchErrorOrDispatch, cache: true, cmd: trackName }); } function loadMetadataTable(response, status) // Handle ajax response (repopulate a metadata val select) { var div = $("div#div_"+this.cmd+"_meta"); var td = $(div).parent('td'); if(td.length > 0 && $(td[0]).width() > 200) { $(td[0]).css('maxWidth',$(td[0]).width() + "px"); $(div).css('overflow','visible'); } $(div).html(response); if(td.length > 0 && $(td[0]).width() > 200) { tr = $(td[0]).parent('tr'); if (tr.length > 0) { if ($(tr).hasClass("bgLevel2")) $(div).children('table').addClass('bgLevel2'); if ($(tr).hasClass("bgLevel3")) $(div).children('table').addClass('bgLevel3'); } } $(div).show(); } function removeHgsid(href) { // remove session id from url parameters if(href.indexOf("?hgsid=") == -1) { href = href.replace(/\&hgsid=\d+/, ""); } else { href = href.replace(/\?hgsid=\d+\&/, "?"); } return href; } ///////////////////////// // Retrieve extra html from a file var gAppendTo = null; function _retrieveHtml(fileName,obj) { // popup cfg dialog if (obj && obj != undefined && $(obj).length == 1) { gAppendTo = obj; } else gAppendTo = null; $.ajax({ type: "GET", url: fileName, dataType: "html", trueSuccess: retrieveHtmlHandle, success: catchErrorOrDispatch, error: errorHandler, cmd: "info", cache: true }); } function retrieveHtml(fileName,obj,toggle) { // Retrieves html from file // If no obj is supplied then html will be shown in a popup. // if obj supplied then object's html will be replaced by what is retrieved. // if obj and toggle == true, and obj has html, it will be emptied, resulting in toggle like calls if (toggle && obj && obj != undefined && $(obj).length == 1) { if ($(obj).html().length > 0) { $(obj).html("") return; } } waitOnFunction( _retrieveHtml, fileName, obj ); // Launches the popup but shields the ajax with a waitOnFunction } function retrieveHtmlHandle(response, status) { // Take html from hgTrackUi and put it up as a modal dialog. // make sure all links (e.g. help links) open up in a new window response = response.replace(/<a /ig, "<a target='_blank' "); // TODO: Shlurp up any javascript files from the response and load them with $.getScript() // example <script type='text/javascript' SRC='../js/tdreszer/jquery.contextmenu-1296177766.js'></script> var cleanHtml = response; var shlurpPattern=/\<script type=\'text\/javascript\' SRC\=\'.*\'\>\<\/script\>/gi; var jsFiles = cleanHtml.match(shlurpPattern); cleanHtml = cleanHtml.replace(shlurpPattern,""); shlurpPattern=/\<script type=\'text\/javascript\'>.*\<\/script\>/gi; var jsEmbeded = cleanHtml.match(shlurpPattern); cleanHtml = cleanHtml.replace(shlurpPattern,""); //<LINK rel='STYLESHEET' href='../style/ui.dropdownchecklist-1276528376.css' TYPE='text/css' /> shlurpPattern=/\<LINK rel=\'STYLESHEET\' href\=\'.*\' TYPE=\'text\/css\' \/\>/gi; var cssFiles = cleanHtml.match(shlurpPattern); cleanHtml = cleanHtml.replace(shlurpPattern,""); if (gAppendTo && gAppendTo != undefined) { //warn($(gAppendTo).html()); //return; $(gAppendTo).html(cleanHtml); return; } var popUp = $('#popupDialog'); if (popUp == undefined || $(popUp).length == 0) { $('body').prepend("<div id='popupDialog' style='display: none'></div>"); popUp = $('#popupDialog'); } $(popUp).html("<div id='pop'>" + cleanHtml + "</div>"); $(popUp).dialog({ ajaxOptions: { cache: true }, // This doesn't work resizable: true, height: 'auto', width: 'auto', minHeight: 200, minWidth: 700, modal: true, closeOnEscape: true, autoOpen: false, buttons: { "OK": function() { $(this).dialog("close"); }}, close: function() { var popUpClose = $('#popupDialog'); if (popUpClose != undefined && $(popUpClose).length == 1) { $(popUpClose).html(""); // clear out html after close to prevent problems caused by duplicate html elements } } }); var myWidth = $(window).width() - 300; if(myWidth > 900) myWidth = 900; $(popUp).dialog("option", "maxWidth", myWidth); $(popUp).dialog("option", "width", myWidth); $(popUp).dialog('open'); var buttOk = $('button.ui-state-default'); if($(buttOk).length == 1) $(buttOk).focus(); } function scrapeVariable(html, name) { // scrape a variable defintion out of html (see jsHelper.c::jsPrintHash) var re = new RegExp("^// START " + name + "\\nvar " + name + " = ([\\S\\s]+);\\n// END " + name + "$", "m"); var a = re.exec(html); var json; if(a && a[1]) { json = eval("(" + a[1] + ")"); } return json; }