2f1f87cbaf30c0fcc4ff3d9bce0f0af8afcd5896 tdreszer Wed May 14 11:24:28 2014 -0700 Polished for jshint. Some minor reformatting included. diff --git src/hg/js/ajax.js src/hg/js/ajax.js index 7dd4026..9013df2 100644 --- src/hg/js/ajax.js +++ src/hg/js/ajax.js @@ -1,515 +1,523 @@ // AJAX Utilities +// Don't complain about line break before '||' etc: +/* jshint -W014 */ + var debug = false; var req; function nullProcessReqChange() { - if(debug && this.readyState == 4) + 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). +// 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 async = false). +// Why? to send several async ajax calls but wait for them all to finish // // 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 + 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) { + if (ajaxWaitCallbackTimeOut) { 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)) + 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) { +// if (formToSubmit) { // //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 +// if (formToSubmit) // 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) +// if (!formToSubmit) // return formSubmitRegister(e.target); // register on first time through // -// if(formToSubmit == "GO") { // Called again as complete +// 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) + if (!callBack) 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) { + } catch(ev) { req = false; } } } if (debug) alert(url); if (req) { req.onreadystatechange = callBack; req.open("GET", url, true); req.send(); //req.send(""); } } function setCartVars(names, values, errFunc, async) { // Asynchronously sets the array of cart vars with values if (names.length <= 0) return; - if (errFunc == null) + if (!errFunc) errFunc = errorHandler; - if (async == null) + if (async === null || async === undefined) // async is boolean so be explicit! async = true; // 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. + // 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, async: async, url: loc, data: data, trueSuccess: function () {}, success: catchErrorOrDispatch, error: errFunc, cache: false }); } function setCartVar(name, value, errFunc, async) { // Asynchronously set a cart variable. setCartVars( [ name ], [ value ], errFunc, async ); } function setVarsFromHash(varHash, errFunc, async) { // 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, errFunc, async); } } 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) + if (!obj) 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"]') + 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. + setTimeout(submitMain(),50); // Delay 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") { + 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." + 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') + 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) { + 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) { + if (obj && $(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 and toggle === true, and obj has html, it will be emptied, thus in toggle like calls + if (toggle && obj && $(obj).length === 1) { if ($(obj).html().length > 0) { - $(obj).html("") + $(obj).html(""); return; } } - waitOnFunction( _retrieveHtml, fileName, obj ); // Launches the popup but shields the ajax with a waitOnFunction + // Launches the popup but shields the ajax with a waitOnFunction + waitOnFunction( _retrieveHtml, fileName, obj ); } 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> + // example <script type='text/javascript' SRC='../js/jquery.contextmenu.js'></script> var cleanHtml = response; - var shlurpPattern=/\<script type=\'text\/javascript\' SRC\=\'.*\'\>\<\/script\>/gi; + 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; + 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; + shlurpPattern=/<LINK rel=\'STYLESHEET\' href\=\'.*\' TYPE=\'text\/css\' \/\>/gi; var cssFiles = cleanHtml.match(shlurpPattern); cleanHtml = cleanHtml.replace(shlurpPattern,""); - if (gAppendTo && gAppendTo != undefined) { + if (gAppendTo) { //warn($(gAppendTo).html()); //return; $(gAppendTo).html(cleanHtml); return; } var popUp = $('#popupDialog'); - if (popUp == undefined || $(popUp).length == 0) { + if (!popUp || $(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 - } + if (popUpClose && $(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) + if ($(buttOk).length === 1) $(buttOk).focus(); } function scrapeVariable(html, name) { // scrape a variable defintion out of html (see jsHelper.c::jsPrintHash) // NOTE: newlines are being removed by some ISPs, so regex must tolerate that: var re = new RegExp("// START " + name + "\\n?var " + name + " = ([\\S\\s]+);" + "\\n?// END " + name, "m"); var a = re.exec(html); var json; if (a && a[1]) { - json = eval("(" + a[1] + ")"); + json = JSON.parse(a[1]); } return json; } // The loadingImage module helps you manage a loading image (for a slow upload; e.g. in hgCustom). var loadingImage = function () { // private vars var imgEle, msgEle, statusMsg; // private methods var refreshLoadingImg = function() { // hack to make sure animation continues in IE after form submission // See: http://stackoverflow.com/questions/774515/keep-an-animated-gif-going-after-form-submits // and http://stackoverflow.com/questions/780560/animated-gif-in-ie-stopping imgEle.attr('src', imgEle.attr('src')); }; // public methods return { init: function(_imgEle, _msgEle, _msg) { - // This should be called from the ready method; imgEle and msgEle should be jQuery objects + // This should be called from the ready method; imgEle and msgEle should be jQuery objs imgEle = _imgEle; msgEle = _msgEle; statusMsg = _msg; - // To make the loadingImg visible on FF, we have to make sure it's visible during page load (i.e. in html) otherwise it doesn't get shown by the submitClick code. + // To make the loadingImg visible on FF, we have to make sure it's visible during + // page load (i.e. in html) otherwise it doesn't get shown by the submitClick code. imgEle.hide(); }, run: function() { msgEle.append(statusMsg); - if(navigator.userAgent.indexOf("Chrome") != -1) { - // In Chrome, gif animation and setTimeout's are stopped when the browser receives the first blank line/comment of the next page - // (basically, the current page is unloaded). I have found no way around this problem, so we just show a - // simple "Processing..." message (we can't make that blink, b/c Chrome doesn't support blinking text). + if (navigator.userAgent.indexOf("Chrome") !== -1) { + // In Chrome, gif animation and setTimeout's are stopped when the browser + // receives the first blank line/comment of the next page (basically, the current + // page is unloaded). I have found no way around this problem, so we just show a + // simple "Processing..." message + // (we can't make that blink, b/c Chrome doesn't support blinking text). // - // (Surprisingly, this is NOT true for Safari, so this is apparently not a WebKit issue). + // (Surprisingly, this is NOT true for Safari, so apparently not a WebKit issue). imgEle.replaceWith("<span id='loadingBlinker'> <b>Processing...</b></span>"); } else { imgEle.show(); setTimeout(refreshLoadingImg, 1000); } }, abort: function() { imgEle.hide(); msgEle.hide(); jQuery('body').css('cursor', ''); } - } + }; }();