// ==UserScript== // @name F5 Frontend Enhancements // @description F5 BIG-IP Frontend UI Enhancements // @match https://*/tmui/Control/* // @homepage https://devcentral.f5.com/s/articles/WebUI-Tweaks // @author https://loadbalancing.se/about // @run-at document-end // @version 25 // @updateURL https://raw.githubusercontent.com/timriker/F5-UI-FrontendEnhancements/master/F5-Frontend-Enhancements.js // @downloadURL https://raw.githubusercontent.com/timriker/F5-UI-FrontendEnhancements/master/F5-Frontend-Enhancements.js // @supportURL https://devcentral.f5.com/s/articles/webui-tweaks-v12-1109 // @grant none // @require https://code.jquery.com/jquery-latest.js // ==/UserScript== /* globals jQuery,$,codeEditor */ /*************************************************************************************** Begin Config section ****************************************************************************************/ /************************************************************** How many rules you want to see in the rule assignment window Default: iRulesCount = 40; ***************************************************************/ var iRulesCount = 40; /************************************************************** How many monitors you want to show in the monitor selection Default: MonitorCount = 30; ***************************************************************/ var MonitorCount = 30; /************************************************************** How many data group list entries to show Default: DatagroupListCount = 30; ***************************************************************/ var DatagroupListCount = 30; /************************************************************** Set http monitor name default suffix Default: HttpMonitorSuffix = ""; ***************************************************************/ var HttpMonitorSuffix = "-http_monitor"; /************************************************************** Set the default pool name Default: DefaultPoolName = ""; ***************************************************************/ var DefaultPoolName = "pool_"; /************************************************************** Set the default action on pool down when creating pools Default: DefaultActionOnPoolDown = 0; Options: 0 = None 1 = Reject 2 = Drop ***************************************************************/ var DefaultActionOnPoolDown = 1; /************************************************************** Set the default action on pool down when creating pools Default = 0; Options: 0 = Round Robin 1 = Ratio (member) 2 = Least Connections (member) 3 = Observed (member) 4 = Predictive (member) 5 = Ratio (node) 6 = Least connections (node) 7 = Fastest (node) 8 = Observed (node) 9 = Predictive (node) 10 = Dynamic Ratio (node) 11 = Fastest (application) 12 = Least sessions 13 = Dynamic ratio (member) 14 = Weighted Least Connections (member) 15 = Weighted Least Connections (node) 16 = Ratio (session) 17 = Ratio Least connections (member) 18 = Ratio Least connections (node) **************************************************************/ var DefaultLBMethod = 4; /************************************************************** Choose Node List as default when creating pools Default: ChooseNodeAsDefault = 0; Options: 0 = No 1 = Yes **************************************************************/ var ChooseNodeAsDefault = 1; /************************************************************** Add default certificate signing alternatives First one defined is always the default one This one is a bit tricky to format, look at the example carefully Options: false = No true = Yes Example that creates two options: var csroptions = { Company1: { OptionName: 'Company 1', CommonName: '[Example *.domain.com]', Division: 'Stockholm office', Organization: 'My Office address', Locality: 'Stockholm', StateProvince: 'Stockholm', Country: 'SE', Email: 'office@company.se', SubjectAlt: '' } , Company2: { OptionName: 'Another company', CommonName: '[Example *.domain.com]', Division: 'Oslo office', Organization: 'My Oslo Office address', Locality: 'Oslo', StateProvince: 'Oslo', Country: 'NO', Email: 'office@company.no', SubjectAlt: '' } } **************************************************************/ var csroptions = { "Company1": { "OptionName": "Company 1", "CommonName": "[Example *.domain.com]", "Division": "Stockholm office", "Organization": "My Office address", "Locality": "Stockholm", "StateProvince": "Stockholm", "Country": "SE", "Email": "office@company.se", "SubjectAlt": "" } , "Company2": { "OptionName": "Another company", "CommonName": "[Example *.domain.com]", "Division": "Oslo office", "Organization": "My Oslo Office address", "Locality": "Oslo", "StateProvince": "Oslo", "Country": "NO", "Email": "office@company.no", "SubjectAlt": "" } } /***************************************************************************** Select this default chain certificate when creating client SSL profiles Default: defaultChain = ""; defaultChain = "/Common/mychain.crt"; *******************************************************************************/ var defaultChain = "/Common/ca-bundle.crt"; /************************************************************************* Chooses a default parent profile when creating client SSL profiles Default: defaultClientSSLParentProfile = ""; defaultClientSSLParentProfile = "/Common/myParentProfile"; ***************************************************************************/ var defaultClientSSLParentProfile = ""; /************************************************************************* Deactivate the choice to activate the Christmas theme altogether Default (allow the choice): allowChristmas = false; Don't allow the choice: allowChristmas = true; ***************************************************************************/ var allowChristmas = true; /************************************************************************** How often should the script update the LTM log stats (in seconds) ltmLogCheckInterval = 30; **************************************************************************/ var ltmLogCheckInterval = 30; /*************************************************************************************** End Config section ****************************************************************************************/ //Make sure that the tampermonkey jQuery does not tamper with F5's scripts this.$ = this.jQuery = jQuery.noConflict(true); //Declare global ajax queue limit var tamperDataGroupLists = new Array(); var detectedarr = []; var poolStatuses; var versionInfo = $(parent.top.document).find("div#deviceid div span").attr("title"); var version = versionInfo.split(" ")[1]; var majorVersion = version.split(".")[0]; var logDatabase; var ltmLogPatterns = { "poolFailures": new function(){ this.enabled = true; this.name = "Pool failures"; this.isMatching = function(event){ return(event.logEvent.match(/^Pool.+monitor status down/) !== null); } }, "nodeFailures": new function(){ this.enabled = true; this.name = "Node failures"; this.isMatching = function(event){ return(event.logEvent.match(/^Node.+monitor status down/) !== null); } }, "errors": new function(){ this.enabled = true; this.name = "Errors"; this.isMatching = function(event){ return(event.logLevel === "error"); } }, "warnings": new function(){ this.enabled = true; this.name = "Warnings"; this.isMatching = function(event){ return(event.logLevel === "warning"); } }, "tclErrors": new function(){ this.enabled = true; this.name = "TCL Errors"; this.isMatching = function(event){ return(event.logEvent.match(/^TCL error/) !== null); } }, "aggressiveMode": new function(){ this.enabled = true; this.name = "Aggressive Mode" this.isMatching = function(event){ return(event.logEvent.match(/aggressive mode activated/) !== null); } }, "addressConflicts": new function(){ this.enabled = true; this.name = "Address Conflicts" this.isMatching = function(event){ return(event.logEvent.match(/address conflict detected for/) !== null); } } } var enhancementFunctions = { "enhanceiRuleProperties": new function(){ // Scans for data group lists in an iRule and adds data group lists on the side this.name = "Improve iRule editor"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/rule/properties.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveiRuleProperties; }, "improveiRuleSelection": new function(){ this.name = "Improve virtual server iRules management"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/form?__handler=/tmui/locallb/virtual_server/resources&__source=Manage") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveiRuleSelection; }, "addHTTPMonitorSuffix": new function(){ this.name = "Adds HTTP monitor suffix to pool names"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return $("select[name=mon_type]").length > 0 && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = addHTTPMonitorSuffix; }, "makeCurrentPartitionObjectsBold": new function(){ this.name = "Make current partition objects bold"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains('/list.jsp') && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = makeCurrentPartitionObjectsBold; }, "improvePoolProperties": new function(){ this.name = "Enhance the pool properties page"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/pool/properties.jsp?name") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improvePoolProperties; }, "improvePoolCreation": new function(){ this.name = "Enhance the pool creation page"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/pool/create.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improvePoolCreation; }, "improvePoolMemberProperties": new function(){ this.name = "Enhance the pool member properties page"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/pool/member/properties.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improvePoolMemberProperties; }, "improveCertKeyChainSelection": new function(){ this.name = "Client SSL Profile enhancements"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return $('input[name="cert_key_chain_override"]').length > 0 && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveCertKeyChainSelection; }, "improveVirtualServerResources": new function(){ this.name = "Improve Virtual Server resource tab"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/virtual_server/resources.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveVirtualServerResources; }, "improveVirtualServerProperties": new function(){ this.name = "Improve Virtual Server properties page"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/virtual_server/properties.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveVirtualServerProperties; }, "improveDataGroupListProperties": new function(){ this.name = "Data group list editing safe guards"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/datagroup/properties.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveDataGroupListProperties; }, "improveDataGroupListEditing": new function(){ this.name = "Add data group list editing features"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return (uriContains("/tmui/Control/jspmap/tmui/locallb/datagroup/properties.jsp") || uriContains("/tmui/Control/jspmap/tmui/locallb/datagroup/create.jsp") || uriContains("/tmui/locallb/datagroup/properties")) && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveDataGroupListEditing; }, "improveClientSSLProfileCreation": new function(){ this.name = "Improves the client SSL profile creation"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/profile/clientssl/create.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improveClientSSLProfileCreation; }, "improvePoolList": new function(){ this.name = "Improves the pool list"; this.description = ` Warning: On very large configurations (~2000 pools) this can be detrimental to the HTTPD process. This is not a risk for the application delivery itself, but may cause the process to be restarted.`; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/pool/list.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = improvePoolList; }, "addPartitionFilter": new function(){ this.name = "Partition filter"; this.description = `` this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return $(parent.top.document).find("input#partitionFilter").length == 0 && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = addPartitionFilter; }, "addChristmasTheme": new function(){ this.name = "Christmas theme"; this.description = `` this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return isItChristmas() && allowChristmas && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = showChristmasOption; }, "addCSRDropDownMenu": new function(){ this.name = "CSR profiles"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/locallb/ssl_certificate/create.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = addCSRDropDownMenu; }, "addLTMLogSummary": new function(){ this.name = "LTM log features"; this.description = ``; this.enabled = true; this.appliesToVersion = ["11", "12", "13", "14", "15"]; this.applicable = function(){ return uriContains("/tmui/Control/jspmap/tmui/overview/welcome/introduction.jsp") && this.appliesToVersion.indexOf(majorVersion) != -1 && this.enabled; }; this.enhance = startLTMLogFetcher; } } for(let i in enhancementFunctions){ let f = enhancementFunctions[i]; if(f.applicable()){ f.enhance(); } } /************************************************************************** * Modify the top frame **************************************************************************/ String.prototype.hashCode = function(){ let hash = 0; if (this.length == 0) return hash; for (let i = 0; i < this.length; i++) { let char = this.charCodeAt(i); hash = ((hash<<5)-hash)+char; hash = hash & hash; // Convert to 32bit integer } return hash; } function startLTMLogFetcher(){ //Check if the database contains anything if(typeof(logDatabase) === "undefined"){ let rawData = localStorage.getItem("ltmLog") || "{\"content\":{},\"lastSynced\":null}"; logDatabase = JSON.parse(rawData); //updateLTMLogStatistics(getLTMLogStatisticsSummary(logDatabase)); initiateLTMLogStatistics(); } if(logDatabase.lastSynced){ let lastSynced = new Date(logDatabase.lastSynced); let now = new Date(); let seconds = (now.getTime() - lastSynced.getTime()) / 1000; } let fetchLTMLog = function(){ $.ajax({ url: "https://" + window.location.host + "/tmui/Control/jspmap/tmui/system/log/list_ltm.jsp", type: "GET", success: function(response) { $(response).find("table.list tbody tr").each(function(){ let message = {} let row = $(this).find("td"); message.timeStamp = $(row[0]).text().trim(); message.logLevel = $(row[1]).text().trim(); message.host = $(row[2]).text().trim(); message.service = $(row[3]).text().trim(); message.statusCode = $(row[4]).text().trim(); message.logEvent = $(row[5]).text().trim(); let data = ""; for(let i in message){ data += message[i] } if(!(data in logDatabase)){ logDatabase.content[data] = message } logDatabase.lastSynced = new Date(); }) updateLTMLogStatistics(getLTMLogStatisticsSummary(logDatabase)); localStorage.setItem("ltmLog", JSON.stringify(logDatabase)); } }) } fetchLTMLog(); setInterval(fetchLTMLog, ltmLogCheckInterval*1000); } function initiateLTMLogStatistics(){ let topFrame = $(parent.top.document); if(topFrame.find("div.ltmLogStats").length == 0){ let styleTag = $(``); topFrame.find('html > head').append(styleTag); let html = ``; let parameterList = []; for(let i in ltmLogPatterns){ if(parameterList.length == 2){ html += `
` + parameterList.join("") + `
` parameterList = []; } parameterList.push(`
Loading...
` ); } if(parameterList.length != 0){ html += `
` + parameterList.join("") + `
` } topFrame.find("div#userinfo").last().after(html); } } function updateLTMLogStatistics(summary){ let topFrame = $(parent.top.document); if(topFrame.find("div.ltmLogStats").length != 0){ let i = 0 for(let stats in summary){ let statsSpan = topFrame.find("div#logStats" + stats + " span"); statsSpan.fadeOut(300); statsSpan.html(summary[stats]); statsSpan.fadeIn(300); } } } function getLTMLogStatisticsSummary(logDatabase){ let summary = {}; let events = logDatabase.content; for(let f in ltmLogPatterns){ let logTest = ltmLogPatterns[f]; if(logTest.enabled){ summary[f] = 0; } } for(let i in events){ let event = events[i]; for(let functionName in ltmLogPatterns){ let f = ltmLogPatterns[functionName]; if(f.isMatching(event)){ summary[functionName]++; } } } return(summary); } function isItChristmas(){ let d = new Date(); return d.getMonth() == 11; } function showChristmasOption(){ if($(parent.top.document).find("input#grinch").length == 0){ let partitionDiv = $(parent.top.document).find("div#partition"); partitionDiv.after(`
Merry Christmas! Which one are you?
Grinch Santa
`); if(localStorage.getItem("tamperMonkey-snowActivated") === "true"){ letItSnow(); $(parent.top.document).find("input#santa").attr("checked", true); } else { $(parent.top.document).find("input#grinch").attr("checked", true); } $(parent.top.document).find("input.christmasButton").on("click", function(){ if($(this).val() === "santa"){ letItSnow(); localStorage.setItem("tamperMonkey-snowActivated", "true"); } else { $(parent.top.document).find("#xmasdiv").remove(); $(parent.top.document).find("#santahat").remove(); localStorage.setItem("tamperMonkey-snowActivated", "false") } }) } } // This function handles the Christmas theme (santa hat on the F5 ball and snow) function letItSnow(){ if(parent.top.document.getElementById("xmasdiv") === null){ let b = parent.top.document.getElementById("banner"); let logo = parent.top.document.getElementById("logo"); let image = $(logo).find("img"); let position = image.position(); let santacap = "data:image/png;base64,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"; $(logo).prepend("
"); $(b).before("
") let canvas = parent.top.document.getElementById("xmassnow"); let w = b.offsetWidth let h = b.offsetHeight let ctx = canvas.getContext('2d'), windowW = w, windowH = h, numFlakes = 200, flakes = []; function Flake(x, y) { let maxWeight = 5, maxSpeed = 0.5; this.x = x; this.y = y; this.r = randomBetween(0, 1); this.a = randomBetween(0, Math.PI); this.aStep = 0.01; this.weight = randomBetween(2, maxWeight); this.alpha = (this.weight / maxWeight); this.speed = (this.weight / maxWeight) * maxSpeed; this.update = function() { this.x += Math.cos(this.a) * this.r; this.a += this.aStep; this.y += this.speed; } } function init() { let i = numFlakes, flake, x, y; while (i--) { x = randomBetween(0, windowW, true); y = randomBetween(0, windowH, true); flake = new Flake(x, y); flakes.push(flake); } scaleCanvas(); loop(); } function scaleCanvas() { canvas.width = windowW; canvas.height = windowH; } function loop() { let i = flakes.length, z, dist, flakeA, flakeB; // clear canvas ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, windowW, windowH); ctx.restore(); // loop of hell while (i--) { flakeA = flakes[i]; flakeA.update(); /*for (z = 0; z < flakes.length; z++) { flakeB = flakes[z]; if (flakeA !== flakeB && distanceBetween(flakeA, flakeB) < 150) { ctx.beginPath(); ctx.moveTo(flakeA.x, flakeA.y); ctx.lineTo(flakeB.x, flakeB.y); ctx.strokeStyle = '#444444'; ctx.stroke(); ctx.closePath(); } }*/ ctx.beginPath(); ctx.arc(flakeA.x, flakeA.y, flakeA.weight, 0, 2 * Math.PI, false); ctx.fillStyle = 'rgba(255, 255, 255, ' + flakeA.alpha + ')'; ctx.fill(); if (flakeA.y >= windowH) { flakeA.y = -flakeA.weight; } } requestAnimationFrame(loop); } function randomBetween(min, max, round) { let num = Math.random() * (max - min + 1) + min; if (round) { return Math.floor(num); } else { return num; } } function distanceBetween(vector1, vector2) { let dx = vector2.x - vector1.x, dy = vector2.y - vector1.y; return Math.sqrt(dx*dx + dy*dy); } init(); } } function addPartitionFilter(){ let partitionDiv = $(parent.top.document).find("div#partition"); // Add the filter input and the label partitionDiv.prepend(" ") partitionDiv.prepend(""); let partitionDropDown = partitionDiv.find("select#partition_control"); let partitonOptions = partitionDropDown.find("option"); let partitionFilterInput = partitionDiv.find("input#partitionFilter"); partitionFilterInput.on("keyup", function(e){ if(e.keyCode === 13){ triggerEvent("change", parent.top.document.querySelector("div#partition select#partition_control")) return; } let searchValue = this.value; // Set the local storage in order to re-populate the filter upon page reload localStorage.setItem("tamperMonkey-PartitionFilter", searchValue); let re = new RegExp(searchValue, "i"); partitonOptions.each(function(){ if($(this).val().match(re) || $(this).val() === "[All]"){ $(this).attr("ismatch", "true") $(this).show(); } else { $(this).attr("ismatch", "false") $(this).hide(); } }); let selectedOption = partitionDropDown.find("option:selected"); let selectedOptionValue = selectedOption.val() || "" let matchedCount = partitionDropDown.find("option[ismatch='true']").length; if(!selectedOptionValue.match(re) && matchedCount > 0){ selectedOption.removeAttr("selected"); partitionDropDown.find("option[ismatch='true']:eq(0)").attr("selected", "selected"); } }) partitionFilterInput.val(localStorage.getItem("tamperMonkey-PartitionFilter") || "").trigger("keyup"); } /************************************************************************** * iRule improvements **************************************************************************/ function improveiRuleProperties(){ // Show the data group lists used in an iRule cacheDataGroupLists(function(dataGroupLists){ //This part prepares the iRule definition table for the data group lists (adds a third column) $("table#general_table thead tr.tablehead td").attr("colspan", 3); $("table#general_table tr").not("#definition_ace_row").each(function(){ $(this).find("td").eq(1).attr("colspan", 2); }); $("tr#definition_ace_row").append("").css({ "vertical-align": "top" }); $("tr#definition_ace_row td.settings").css("width","80%"); //This command generates the data group lists (if any) getDataGroupListsFromRule($("textarea#rule_definition").val()); //getDataGroupListsFromRuleOld($("textarea#rule_definition").val()); //Update the list on every key stroke $(document).on("keyup", function(){ let iRuleContent = codeEditor.gSettings.editor.container.env.document.doc.$lines.join("\n"); getDataGroupListsFromRule(iRuleContent); //getDataGroupListsFromRuleOld($("textarea#rule_definition").val()); }); }); } // Caches a list of all the data group lists available in Common and the current partition (if any) function cacheDataGroupLists(updateDGPage){ let DataGroupListLink = "https://" + window.location.host + "/tmui/Control/jspmap/tmui/locallb/datagroup/list.jsp"; // We want to get all data group lists in case there is a direct reference let currentPartition = getCookie("F5_CURRENT_PARTITION"); replaceCookie("F5_CURRENT_PARTITION", "\"[All]\""); //Request the iRule page to see if the instance exists or not $.ajax({ url: DataGroupListLink, type: "GET", success: function(response) { let dataGroupListLinks = $(response).find('table.list tbody#list_body tr td:nth-child(3) a'); for(let i = 0; i < dataGroupListLinks.length; i++){ let link = dataGroupListLinks[i].href; let name = link.split("name=")[1]; tamperDataGroupLists.push(name); } replaceCookie("F5_CURRENT_PARTITION", currentPartition); updateDGPage(); } }); } //Parses data group list html to get the key/value pairs for the hover information function parseDataGroupValues(dg, showBalloon){ let dgLink = 'https://' + window.location.host + '/tmui/Control/jspmap/tmui/locallb/datagroup/properties.jsp?name=' + dg; let html; $.ajax({ url: dgLink, type: "GET", success: function(htmlresponse) { let matches = htmlresponse.match(/