Index: binaries/data/mods/public/gui/session/selection_panels.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels.js	(revision 18243)
+++ binaries/data/mods/public/gui/session/selection_panels.js	(working copy)
@@ -9,7 +9,7 @@
  * the item and some other standard data is added to a data object.
  *
  * The standard data is
- * var data = {
+ * {
  *   "i":              index
  *   "item":           item coming from the getItems function
  *   "selection":      list of currently selected items
@@ -23,24 +23,15 @@
  *   "countDisplay":   gui caption space
  * };
  *
- * Then, addData is called, and can be used to abort the processing
- * of the current item by returning false.
- * It should return true if you want the panel to be filled.
- *
- * addData is used to add data to the data object on top
- * (or instead of) the standard data.
- * addData is not obligated, the function will just continue
- * with the content setters if no addData is present.
- *
- * After the addData, all functions starting with "set" are called.
- * These are used to set various parts of content.
+ * Then for every data object, the setupButton function is called which
+ * sets the view and handlers of the button.
  */
 
 /* cache some formation info */
-var g_availableFormations = new Map();   // available formations per player
-var g_formationsInfo = new Map();
+let g_availableFormations = new Map();   // available formations per player
+let g_formationsInfo = new Map();
 
-var g_SelectionPanels = {};
+let g_SelectionPanels = {};
 
 // ALERT
 g_SelectionPanels.Alert = {
@@ -54,8 +45,9 @@
 			return [];
 		return ["increase", "end"];
 	},
-	"setAction": function(data)
+	"setupButton": function(data)
 	{
+		// Action
 		data.button.onPress = function() {
 			if (data.item == "increase")
 				increaseAlertLevel();
@@ -62,9 +54,8 @@
 			else if (data.item == "end")
 				endOfAlert();
 		};
-	},
-	"setTooltip": function(data)
-	{
+
+		// Tooltip
 		if (data.item == "increase")
 		{
 			if (data.unitEntState.alertRaiser.hasRaisedAlert)
@@ -74,9 +65,8 @@
 		}
 		else if (data.item == "end")
 			data.button.tooltip = translate("End of alert.");
-	},
-	"setGraphics": function(data)
-	{
+
+		// Graphics
 		if (data.item == "increase")
 		{
 			data.button.hidden = !data.unitEntState.alertRaiser.canIncreaseLevel;
@@ -91,6 +81,10 @@
 			data.icon.sprite = "stretched:session/icons/bell_level0.png";
 		}
 		data.button.enabled = !data.button.hidden && controlsPlayer(data.unitEntState.player);
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
+		return true;
 	}
 };
 
@@ -108,57 +102,53 @@
 		// ["food", "wood", "stone", "metal"]
 		return BARTER_RESOURCES;
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
+		// Get Gui objects
 		// data.item is the resource name in this case
-		data.button = {};
-		data.icon = {};
-		data.amount = {};
-		for (var a of BARTER_ACTIONS)
+		let button = {};
+		let icon = {};
+		let amount = {};
+		for (let a of BARTER_ACTIONS)
 		{
-			data.button[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Button["+data.i+"]");
-			data.icon[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Icon["+data.i+"]");
-			data.amount[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Amount["+data.i+"]");
+			button[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Button[" + data.i + "]");
+			icon[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Icon[" + data.i + "]");
+			amount[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Amount[" + data.i + "]");
 		}
-		data.selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection["+data.i+"]");
+		let selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection[" + data.i + "]");
 
-		data.amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
+		let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
 		if (Engine.HotkeyIsPressed("session.massbarter"))
-			data.amountToSell *= BARTER_BUNCH_MULTIPLIER;
-		data.isSelected = data.item == g_barterSell;
-		return true;
-	},
-	"setCountDisplay": function(data)
-	{
-		data.amount.Sell.caption = "-" + data.amountToSell;
-		var sellPrice = data.unitEntState.barterMarket.prices.sell[g_barterSell];
-		var buyPrice = data.unitEntState.barterMarket.prices.buy[data.item];
-		data.amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * data.amountToSell);
-	},
-	"setTooltip": function(data)
-	{
-		var resource = getLocalizedResourceName(data.item, "withinSentence");
-		data.button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
-		data.button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
-	},
-	"setAction": function(data)
-	{
-		data.button.Sell.onPress = function() { g_barterSell = data.item; };
-		var exchangeResourcesParameters = {
+			amountToSell *= BARTER_BUNCH_MULTIPLIER;
+
+		// Caption
+		amount.Sell.caption = "-" + amountToSell;
+		let sellPrice = data.unitEntState.barterMarket.prices.sell[g_barterSell];
+		let buyPrice = data.unitEntState.barterMarket.prices.buy[data.item];
+		amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * amountToSell);
+
+		// Tooltip
+		let resource = getLocalizedResourceName(data.item, "withinSentence");
+		button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
+		button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
+
+		// Action
+		button.Sell.onPress = function() { g_barterSell = data.item; };
+		let exchangeResourcesParameters = {
 			"sell": g_barterSell,
 			"buy": data.item,
-			"amount": data.amountToSell
+			"amount": amountToSell
 		};
-		data.button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
-	},
-	"setGraphics": function(data)
-	{
-		var grayscale = data.isSelected ? "color: 0 0 0 100:grayscale:" : "";
+		button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
 
+		// Graphics
+		let isSelected = data.item == g_barterSell;
+		let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : "";
+
 		// do we have enough of this resource to sell?
-		var neededRes = {};
-		neededRes[data.item] = data.amountToSell;
-		var canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
+		let neededRes = {};
+		neededRes[data.item] = amountToSell;
+		let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
 			"cost": neededRes,
 			"player": data.unitEntState.player
 		}) ? "color:255 0 0 80:" : "";
@@ -165,24 +155,24 @@
 
 		// Let's see if we have enough resources to barter.
 		neededRes = {};
-		neededRes[g_barterSell] = data.amountToSell;
-		var canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
+		neededRes[g_barterSell] = amountToSell;
+		let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
 			"cost": neededRes,
 			"player": data.unitEntState.player
 		}) ? "color:255 0 0 80:" : "";
 
-		data.icon.Sell.sprite = canSellCurrent + "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
-		data.icon.Buy.sprite = canBuyAny + "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
+		icon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
+		icon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
 
-		data.button.Buy.hidden = data.isSelected;
-		data.button.Buy.enabled = controlsPlayer(data.unitEntState.player);
-		data.button.Sell.hidden = false;
-		data.selectionIcon.hidden = !data.isSelected;
-	},
-	"setPosition": function(data)
-	{
-		setPanelObjectPosition(data.button.Sell, data.i, data.rowLength);
-		setPanelObjectPosition(data.button.Buy, data.i + data.rowLength, data.rowLength);
+		button.Buy.hidden = isSelected;
+		button.Buy.enabled = controlsPlayer(data.unitEntState.player);
+		button.Sell.hidden = false;
+		selectionIcon.hidden = !isSelected;
+
+		// Position
+		setPanelObjectPosition(button.Sell, data.i, data.rowLength);
+		setPanelObjectPosition(button.Buy, data.i + data.rowLength, data.rowLength);
+		return true;
 	}
 };
 
@@ -198,7 +188,7 @@
 
 		for (let c in g_EntityCommands)
 		{
-			var info = g_EntityCommands[c].getInfo(unitEntState);
+			let info = g_EntityCommands[c].getInfo(unitEntState);
 			if (!info)
 				continue;
 
@@ -207,12 +197,12 @@
 		}
 		return commands;
 	},
-	"setTooltip": function(data)
+	"setupButton": function(data)
 	{
+		// Tooltip
 		data.button.tooltip = data.item.tooltip;
-	},
-	"setAction": function(data)
-	{
+
+		// Action
 		data.button.onPress = function() {
 			if (data.item.callback)
 				data.item.callback(data.item);
@@ -219,22 +209,19 @@
 			else
 				performCommand(data.unitEntState.id, data.item.name);
 		};
-	},
-	"setCountDisplay": function(data)
-	{
+
+		// Count
 		data.countDisplay.caption = data.item.count || "";
-	},
-	"setGraphics": function(data)
-	{
+
+		// Graphics
 		data.button.enabled = controlsPlayer(data.unitEntState.player);
 		let grayscale = data.button.enabled ? "" : "grayscale:";
 		data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon;
-	},
-	"setPosition": function(data)
-	{
-		var size = data.button.size;
+
+		// Position
+		let size = data.button.size;
 		// count on square buttons, so size.bottom is the width too
-		var spacer = size.bottom + 1;
+		let spacer = size.bottom + 1;
 		// relative to the center ( = 50%)
 		size.rleft = size.rright = 50;
 		// offset from the center calculation
@@ -241,6 +228,7 @@
 		size.left = (data.i - data.numberOfItems/2) * spacer;
 		size.right = size.left + size.bottom;
 		data.button.size = size;
+		return true;
 	}
 };
 
@@ -250,12 +238,13 @@
 	{
 		return 2;
 	},
+	"conflictsWith": ["Command"],
 	"getItems": function(unitEntState)
 	{
-		var commands = [];
-		for (var c in g_AllyEntityCommands)
+		let commands = [];
+		for (let c in g_AllyEntityCommands)
 		{
-			var info = g_AllyEntityCommands[c].getInfo(unitEntState);
+			let info = g_AllyEntityCommands[c].getInfo(unitEntState);
 			if (!info)
 				continue;
 			info.name = c;
@@ -263,12 +252,12 @@
 		}
 		return commands;
 	},
-	"setTooltip": function(data)
+	"setupButton": function(data)
 	{
+		// Tooltip
 		data.button.tooltip = data.item.tooltip;
-	},
-	"setAction": function(data)
-	{
+
+		// Action
 		data.button.onPress = function() {
 			if (data.item.callback)
 				data.item.callback(data.item);
@@ -275,23 +264,19 @@
 			else
 				performAllyCommand(data.unitEntState.id, data.item.name);
 		};
-	},
-	"conflictsWith": ["Command"],
-	"setCountDisplay": function(data)
-	{
+
+		// Count
 		data.countDisplay.caption = data.item.count || "";
-	},
-	"setGraphics": function(data)
-	{
+
+		// Graphics
 		data.button.enabled = data.item.count != undefined && data.item.count > 0;
 		let grayscale = data.button.enabled ? "" : "grayscale:";
 		data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon;
-	},
-	"setPosition": function(data)
-	{
-		var size = data.button.size;
+
+		// Position
+		let size = data.button.size;
 		// count on square buttons, so size.bottom is the width too
-		var spacer = size.bottom + 1;
+		let spacer = size.bottom + 1;
 		// relative to the center ( = 50%)
 		size.rleft = size.rright = 50;
 		// offset from the center calculation
@@ -298,6 +283,10 @@
 		size.left = (data.i - data.numberOfItems/2) * spacer;
 		size.right = size.left + size.bottom;
 		data.button.size = size;
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
+		return true;
 	}
 };
 
@@ -311,84 +300,78 @@
 	{
 		return getAllBuildableEntitiesFromSelection();
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
-		data.entType = data.item;
-		data.template = GetTemplateData(data.entType);
-		if (!data.template) // abort if no template
+		let template = GetTemplateData(data.item);
+		if (!template) // abort if no template
 			return false;
 
-		data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
-			"tech": data.template.requiredTechnology,
+		let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
+			"tech": template.requiredTechnology,
 			"player": data.unitEntState.player
 		});
 
-		if (data.template.cost)
-			data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
-				"cost": multiplyEntityCosts(data.template, 1),
+		let neededResources;
+		if (template.cost)
+			neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
+				"cost": multiplyEntityCosts(template, 1),
 				"player": data.unitEntState.player
 			});
 
-		data.limits = getEntityLimitAndCount(data.playerState, data.entType);
+		let limits = getEntityLimitAndCount(data.playerState, data.item);
 
-		if (data.template.wallSet)
-			data.template.auras = GetTemplateData(data.template.wallSet.templates.long).auras;
+		if (template.wallSet)
+			template.auras = GetTemplateData(template.wallSet.templates.long).auras;
 
-		return true;
-	},
-	"setAction": function(data)
-	{
+		// Action
 		data.button.onPress = function () { startBuildingPlacement(data.item, data.playerState); };
-	},
-	"setTooltip": function(data)
-	{
-		var tooltip = getEntityNamesFormatted(data.template);
-		tooltip += getVisibleEntityClassesFormatted(data.template);
-		tooltip += getAurasTooltip(data.template);
 
-		if (data.template.tooltip)
-			tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
+		// Tooltip
+		let tooltip = getEntityNamesFormatted(template);
+		tooltip += getVisibleEntityClassesFormatted(template);
+		tooltip += getAurasTooltip(template);
 
-		tooltip += "\n" + getEntityCostTooltip(data.template);
-		tooltip += getPopulationBonusTooltip(data.template);
+		if (template.tooltip)
+			tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
 
-		tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
+		tooltip += "\n" + getEntityCostTooltip(template);
+		tooltip += getPopulationBonusTooltip(template);
 
-		if (!data.technologyEnabled)
+		tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
+
+		if (!technologyEnabled)
 			tooltip += "\n" + sprintf(translate("Requires %(technology)s"), {
-				"technology": getEntityNames(GetTechnologyData(data.template.requiredTechnology))
+				"technology": getEntityNames(GetTechnologyData(template.requiredTechnology))
 			});
 
-		if (data.neededResources)
-			tooltip += getNeededResourcesTooltip(data.neededResources);
+		if (neededResources)
+			tooltip += getNeededResourcesTooltip(neededResources);
 
 		data.button.tooltip = tooltip;
-		return true;
-	},
-	"setGraphics": function(data)
-	{
-		var modifier = "";
-		if (!data.technologyEnabled || data.limits.canBeAddedCount == 0)
+
+		// Graphics
+		let modifier = "";
+		if (!technologyEnabled || limits.canBeAddedCount == 0)
 		{
 			data.button.enabled = false;
 			modifier += "color: 0 0 0 127:";
 			modifier += "grayscale:";
 		}
-		else if (data.neededResources)
+		else if (neededResources)
 		{
 			data.button.enabled = false;
-			modifier += resourcesToAlphaMask(data.neededResources) +":";
+			modifier += resourcesToAlphaMask(neededResources) +":";
 		}
 		else
 			data.button.enabled = controlsPlayer(data.unitEntState.player);
 
-		if (data.template.icon)
-			data.icon.sprite = modifier + "stretched:session/portraits/" + data.template.icon;
-	},
-	"setPosition": function(data)
-	{
-		var index = data.i + getNumberOfRightPanelButtons();
+		if (template.icon)
+			data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
+
+		// Position
+		let index = data.i + getNumberOfRightPanelButtons();
 		setPanelObjectPosition(data.button, index, data.rowLength);
+		return true;
 	}
 };
 
@@ -408,35 +391,37 @@
 			g_availableFormations.set(unitEntState.player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntState.player));
 		return g_availableFormations.get(unitEntState.player);
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
+		// Get Data
 		if (!g_formationsInfo.has(data.item))
 			g_formationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item }));
-		data.formationInfo = g_formationsInfo.get(data.item);
-		data.formationOk = canMoveSelectionIntoFormation(data.item);
-		data.formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
+
+		let formationInfo = g_formationsInfo.get(data.item);
+		let formationOk = canMoveSelectionIntoFormation(data.item);
+		let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
 			"ents": data.selection,
 			"formationTemplate": data.item
 		});
-		return true;
-	},
-	"setAction": function(data)
-	{
+
+		// Action
 		data.button.onPress = function() { performFormation(data.unitEntState.id, data.item); };
-	},
-	"setTooltip": function(data)
-	{
-		var tooltip = translate(data.formationInfo.name);
-		if (!data.formationOk && data.formationInfo.tooltip)
-			tooltip += "\n" + "[color=\"red\"]" + translate(data.formationInfo.tooltip) + "[/color]";
+
+		// Tooltip
+		let tooltip = translate(formationInfo.name);
+		if (!formationOk && formationInfo.tooltip)
+			tooltip += "\n" + "[color=\"red\"]" + translate(formationInfo.tooltip) + "[/color]";
 		data.button.tooltip = tooltip;
-	},
-	"setGraphics": function(data)
-	{
-		data.button.enabled = data.formationOk && controlsPlayer(data.unitEntState.player);
-		var grayscale = data.formationOk ? "" : "grayscale:";
-		data.guiSelection.hidden = !data.formationSelected;
-		data.icon.sprite = "stretched:"+grayscale+"session/icons/"+data.formationInfo.icon;
+
+		// Graphics
+		data.button.enabled = formationOk && controlsPlayer(data.unitEntState.player);
+		let grayscale = formationOk ? "" : "grayscale:";
+		data.guiSelection.hidden = !formationSelected;
+		data.icon.sprite = "stretched:" + grayscale + "session/icons/" + formationInfo.icon;
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
+		return true;
 	}
 };
 
@@ -451,44 +436,37 @@
 	{
 		if (!unitEntState.garrisonHolder)
 			return [];
-		var groups = new EntityGroups();
-		for (var ent of selection)
+		let groups = new EntityGroups();
+		for (let ent of selection)
 		{
-			var state = GetEntityState(ent);
+			let state = GetEntityState(ent);
 			if (state.garrisonHolder)
 				groups.add(state.garrisonHolder.entities);
 		}
 		return groups.getEntsGrouped();
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
-		data.entType = data.item.template;
-		data.template = GetTemplateData(data.entType);
-		if (!data.template)
+		// Get Data
+		let template = GetTemplateData(data.item.template);
+		if (!template)
 			return false;
-		data.name = getEntityNames(data.template);
-		data.count = data.item.ents.length;
-		return true;
-	},
-	"setAction": function(data)
-	{
+
+		// Action
 		data.button.onPress = function() { unloadTemplate(data.item.template); };
-	},
-	"setTooltip": function(data)
-	{
-		var tooltip = sprintf(translate("Unload %(name)s"), { "name": data.name }) + "\n";
+
+		// Tooltip
+		let tooltip = sprintf(translate("Unload %(name)s"), { "name": getEntityNames(template) }) + "\n";
 		tooltip += translate("Single-click to unload 1. Shift-click to unload all of this type.");
 		data.button.tooltip = tooltip;
-	},
-	"setCountDisplay": function(data)
-	{
-		data.countDisplay.caption = data.count || "";
-	},
-	"setGraphics": function(data)
-	{
-		var grayscale = "";
-		var ents = data.item.ents;
-		var entplayer = GetEntityState(ents[0]).player;
+
+		// Count
+		data.countDisplay.caption = data.item.ents.length || "";
+
+		// Graphics
+		let grayscale = "";
+		let ents = data.item.ents;
+		let entplayer = GetEntityState(ents[0]).player;
 		data.button.sprite = "color:" + rgbToGuiColor(g_Players[entplayer].color) +":";
 
 		if (!controlsPlayer(data.unitEntState.player) && !controlsPlayer(entplayer))
@@ -497,7 +475,11 @@
 			grayscale = "grayscale:";
 		}
 
-		data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + data.template.icon;
+		data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
+		return true;
 	}
 };
 
@@ -510,18 +492,18 @@
 	"getItems": function(unitEntState, selection)
 	{
 		// Allow long wall pieces to be converted to gates
-		var longWallTypes = {};
-		var walls = [];
-		var gates = [];
-		for (var ent of selection)
+		let longWallTypes = {};
+		let walls = [];
+		let gates = [];
+		for (let ent of selection)
 		{
-			var state = GetEntityState(ent);
+			let state = GetEntityState(ent);
 			if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
 			{
-				var gateTemplate = getWallGateTemplate(state.id);
+				let gateTemplate = getWallGateTemplate(state.id);
 				if (gateTemplate)
 				{
-					var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
+					let tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
 					if (!tooltipString)
 					{
 						warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
@@ -554,26 +536,25 @@
 			}
 			// Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
 			else if (state.gate && state.gate.locked != gates[0].gate.locked)
-				for (var j = 0; j < gates.length; ++j)
+				for (let j = 0; j < gates.length; ++j)
 					delete gates[j].gate.locked;
 		}
 
 		// Place wall conversion options after gate lock/unlock icons.
-		var items = gates.concat(walls);
-		return items;
+		return gates.concat(walls);
 	},
-	"setAction": function(data)
+	"setupButton": function(data)
 	{
+		// Action
 		data.button.onPress = function() {data.item.callback(data.item); };
-	},
-	"setTooltip": function(data)
-	{
-		var tooltip = data.item.tooltip;
+
+		// Tooltip
+		let tooltip = data.item.tooltip;
 		if (data.item.template)
 		{
 			data.template = GetTemplateData(data.item.template);
 			data.wallCount = data.selection.reduce(function (count, ent) {
-					var state = GetEntityState(ent);
+					let state = GetEntityState(ent);
 					if (hasClass(state, "LongWall") && !state.gate)
 						++count;
 					return count;
@@ -589,11 +570,10 @@
 				tooltip += getNeededResourcesTooltip(data.neededResources);
 		}
 		data.button.tooltip = tooltip;
-	},
-	"setGraphics": function(data)
-	{
+
+		// Graphics
 		data.button.enabled = controlsPlayer(data.unitEntState.player);
-		var gateIcon;
+		let gateIcon;
 		if (data.item.gate)
 		{
 			// If already a gate, show locking actions
@@ -606,19 +586,19 @@
 		else
 		{
 			// otherwise show gate upgrade icon
-			var template = GetTemplateData(data.item.template);
+			let template = GetTemplateData(data.item.template);
 			if (!template)
-				return;
+				return false;
 			gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
 			data.guiSelection.hidden = true;
 		}
 
 		data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon;
-	},
-	"setPosition": function(data)
-	{
-		var index = data.i + getNumberOfRightPanelButtons();
+
+		// Position
+		let index = data.i + getNumberOfRightPanelButtons();
 		setPanelObjectPosition(data.button, index, data.rowLength);
+		return true;
 	}
 };
 
@@ -630,10 +610,10 @@
 	},
 	"getItems": function(unitEntState, selection)
 	{
-		var checks = {};
-		for (var ent of selection)
+		let checks = {};
+		for (let ent of selection)
 		{
-			var state = GetEntityState(ent);
+			let state = GetEntityState(ent);
 			if (!state.pack)
 				continue;
 			if (state.pack.progress == 0)
@@ -652,7 +632,7 @@
 					checks.unpackCancelButton = true;
 			}
 		}
-		var items = [];
+		let items = [];
 		if (checks.packButton)
 			items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
 		if (checks.unpackButton)
@@ -663,16 +643,15 @@
 			items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
 		return items;
 	},
-	"setAction": function(data)
+	"setupButton": function(data)
 	{
+		// Action
 		data.button.onPress = function() {data.item.callback(data.item); };
-	},
-	"setTooltip": function(data)
-	{
+
+		// Tooltip
 		data.button.tooltip = data.item.tooltip;
-	},
-	"setGraphics": function(data)
-	{
+
+		// Graphics
 		if (data.item.packing)
 			data.icon.sprite = "stretched:session/icons/cancel.png";
 		else if (data.item.packed)
@@ -681,11 +660,11 @@
 			data.icon.sprite = "stretched:session/icons/pack.png";
 
 		data.button.enabled = controlsPlayer(data.unitEntState.player);
-	},
-	"setPosition": function(data)
-	{
-		var index = data.i + getNumberOfRightPanelButtons();
+
+		// Position
+		let index = data.i + getNumberOfRightPanelButtons();
 		setPanelObjectPosition(data.button, index, data.rowLength);
+		return true;
 	}
 };
 
@@ -701,38 +680,31 @@
 	},
 	"resizePanel": function(numberOfItems, rowLength)
 	{
-		var numRows = Math.ceil(numberOfItems / rowLength);
-		var panel = Engine.GetGUIObjectByName("unitQueuePanel");
-		var size = panel.size;
-		var buttonSize = Engine.GetGUIObjectByName("unitQueueButton[0]").size.bottom;
-		var margin = 4;
+		let numRows = Math.ceil(numberOfItems / rowLength);
+		let panel = Engine.GetGUIObjectByName("unitQueuePanel");
+		let size = panel.size;
+		let buttonSize = Engine.GetGUIObjectByName("unitQueueButton[0]").size.bottom;
+		let margin = 4;
 		size.top = size.bottom - numRows*buttonSize - (numRows+2)*margin;
 		panel.size = size;
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
 		// differentiate between units and techs
+		let template;
 		if (data.item.unitTemplate)
-		{
-			data.entType = data.item.unitTemplate;
-			data.template = GetTemplateData(data.entType);
-		}
+			template = GetTemplateData(data.item.unitTemplate);
 		else if (data.item.technologyTemplate)
-		{
-			data.entType = data.item.technologyTemplate;
-			data.template = GetTechnologyData(data.entType);
-		}
-		data.progress = Math.round(data.item.progress*100) + "%";
+			template = GetTechnologyData(data.item.technologyTemplate);
 
-		return data.template;
-	},
-	"setAction": function(data)
-	{
+		if (!template)
+			return false;
+
+		// Action
 		data.button.onPress = function() { removeFromProductionQueue(data.item.producingEnt, data.item.id); };
-	},
-	"setTooltip": function(data)
-	{
-		var tooltip = getEntityNames(data.template);
+
+		// Tooltip
+		let tooltip = getEntityNames(template);
 		if (data.item.neededSlots)
 		{
 			tooltip += "\n[color=\"red\"]" + translate("Insufficient population capacity:") + "\n[/color]";
@@ -742,30 +714,30 @@
 			});
 		}
 		data.button.tooltip = tooltip;
-	},
-	"setCountDisplay": function(data)
-	{
+
+		// Count
 		data.countDisplay.caption = data.item.count > 1 ? data.item.count : "";
-	},
-	"setProgressDisplay": function(data)
-	{
+
+		// Graphics
 		// show the progress number for the first item
 		if (data.i == 0)
-			Engine.GetGUIObjectByName("queueProgress").caption = data.progress;
+			Engine.GetGUIObjectByName("queueProgress").caption = Math.round(data.item.progress*100) + "%";
 
-		var guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
-		var size = guiObject.size;
+		let guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
+		let size = guiObject.size;
 
 		// Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
 		size.top = size.left + Math.round(data.item.progress * (size.right - size.left));
 		guiObject.size = size;
-	},
-	"setGraphics": function(data)
-	{
-		if (data.template.icon)
-			data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
 
+		if (template.icon)
+			data.icon.sprite = "stretched:session/portraits/" + template.icon;
+
 		data.button.enabled = controlsPlayer(data.unitEntState.player);
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
+		return true;
 	}
 };
 
@@ -795,141 +767,126 @@
 	},
 	"hideItem": function(i, rowLength) // called when no item is found
 	{
-		Engine.GetGUIObjectByName("unitResearchButton["+i+"]").hidden = true;
+		Engine.GetGUIObjectByName("unitResearchButton[" + i + "]").hidden = true;
 		// We also remove the paired tech and the pair symbol
-		Engine.GetGUIObjectByName("unitResearchButton["+(i+rowLength)+"]").hidden = true;
-		Engine.GetGUIObjectByName("unitResearchPair["+i+"]").hidden = true;
+		Engine.GetGUIObjectByName("unitResearchButton[" + (i + rowLength) + "]").hidden = true;
+		Engine.GetGUIObjectByName("unitResearchPair[" + i + "]").hidden = true;
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
+		// Get Data
 		if (!data.item.tech)
 		{
 			g_SelectionPanels.Research.hideItem(data.i, data.rowLength);
 			return false;
 		}
-		data.entType = data.item.tech.pair ? [data.item.tech.top, data.item.tech.bottom] : [data.item.tech];
-		data.template = data.entType.map(GetTechnologyData);
-		// abort if no template found for any of the techs
-		if (data.template.some(v => !v))
-			return false;
+		let techs = data.item.tech.pair ? [data.item.tech.bottom, data.item.tech.top] : [data.item.tech];
 
-		for (let template of data.template)
-			for (let res in template.cost)
-				template.cost[res] *= data.item.techCostMultiplier[res];
+		// start position (start at the bottom)
+		let position = data.i + data.rowLength;
 
-		// index one row below
-		var shiftedIndex = data.i + data.rowLength;
-		data.positions = data.item.tech.pair ? [data.i, shiftedIndex] : [shiftedIndex];
-		data.positionsToHide = data.item.tech.pair ? [] : [data.i];
+		// only show the top button for pairs
+		if (!data.item.tech.pair)
+			Engine.GetGUIObjectByName("unitResearchButton[" + data.i + "]").hidden = true;
 
-		// add top buttons to the data
-		data.button = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchButton["+p+"]"));
-		data.buttonsToHide = data.positionsToHide.map(p => Engine.GetGUIObjectByName("unitResearchButton["+p+"]"));
+		// set up the tech connector
+		let pair = Engine.GetGUIObjectByName("unitResearchPair[" + data.i + "]");
+		pair.hidden = data.item.tech.pair == null;
+		setPanelObjectPosition(pair, data.i, data.rowLength);
 
-		data.icon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchIcon["+p+"]"));
-		data.unchosenIcon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchUnchosenIcon["+p+"]"));
+		// handle one or two techs
+		for (let i in techs)
+		{
+			let tech = techs[i];
+			let template = GetTechnologyData(tech);
+			if (!template)
+				return false;
 
-		data.neededResources = data.template.map(t => Engine.GuiInterfaceCall("GetNeededResources", {
-			"cost": t.cost,
-			"player": data.unitEntState.player
-		}));
+			for (let res in template.cost)
+				template.cost[res] *= data.item.techCostMultiplier[res];
 
-		data.requirementsPassed = data.entType.map(e => Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
-			"tech": e,
-			"player": data.unitEntState.player
-		}));
+			let neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
+				"cost": template.cost,
+				"player": data.unitEntState.player
+			});
 
-		data.pair = Engine.GetGUIObjectByName("unitResearchPair["+data.i+"]");
+			let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
+				"tech": tech,
+				"player": data.unitEntState.player
+			});
 
-		return true;
-	},
-	"setTooltip": function(data)
-	{
-		for (var i in data.entType)
-		{
-			var tooltip = "";
-			var template = data.template[i];
+			let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]");
+			let icon = Engine.GetGUIObjectByName("unitResearchIcon[" + position + "]");
+
+			// Tooltip
+			let tooltip = "";
 			tooltip = getEntityNamesFormatted(template);
 			if (template.tooltip)
 				tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
 
 			tooltip += "\n" + getEntityCostTooltip(template);
-			if (!data.requirementsPassed[i])
+			if (!requirementsPassed)
 			{
 				tooltip += "\n" + template.requirementsTooltip;
 				if (template.classRequirements)
 				{
-					var player = data.unitEntState.player;
-					var current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
-					var remaining = template.classRequirements.number - current;
+					let player = data.unitEntState.player;
+					let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
+					let remaining = template.classRequirements.number - current;
 					tooltip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { "number": remaining });
 				}
 			}
-			if (data.neededResources[i])
-				tooltip += getNeededResourcesTooltip(data.neededResources[i]);
-			data.button[i].tooltip = tooltip;
-		}
-	},
-	"setAction": function(data)
-	{
-		for (var i in data.entType)
-		{
-			// array containing the indices other buttons
-			var others = Object.keys(data.template);
-			others.splice(i, 1);
-			var button = data.button[i];
-			// as we're in a loop, we need to limit the scope with a closure
-			// else the last value of the loop will be taken, rather than the current one
-			button.onpress = (function(template) { return function () { addResearchToQueue(data.unitEntState.id, template); }; })(data.entType[i]);
-			// on mouse enter, show a cross over the other icons
-			button.onmouseenter = (function(others, icons) {
-				return function() {
-					for (var j of others)
-						icons[j].hidden = false;
+			if (neededResources)
+				tooltip += getNeededResourcesTooltip(neededResources);
+			button.tooltip = tooltip;
+
+			// Action
+			button.onpress = function () {
+				addResearchToQueue(data.unitEntState.id, tech);
+			};
+
+			if (data.item.tech.pair)
+			{
+				// on mouse enter, show a cross over the other icon
+				let otherPosition = (position + data.rowLength) % (2 * data.rowLength);
+				let unchosenIcon = Engine.GetGUIObjectByName("unitResearchUnchosenIcon[" + otherPosition + "]");
+				button.onmouseenter = function() {
+						unchosenIcon.hidden = false;
 				};
-			})(others, data.unchosenIcon);
-			button.onmouseleave = (function(others, icons) {
-				return function() {
-					for (var j of others)
-						icons[j].hidden = true;
+				button.onmouseleave = function() {
+						unchosenIcon.hidden = true;
 				};
-			})(others, data.unchosenIcon);
-		}
-	},
-	"setGraphics": function(data)
-	{
-		for (var i in data.entType)
-		{
-			let button = data.button[i];
+			}
+
+			// Graphics
 			button.hidden = false;
-			var modifier = "";
-			if (!data.requirementsPassed[i])
+			let modifier = "";
+			if (!requirementsPassed)
 			{
 				button.enabled = false;
 				modifier += "color: 0 0 0 127:";
 				modifier += "grayscale:";
 			}
-			else if (data.neededResources[i])
+			else if (neededResources)
 			{
 				button.enabled = false;
-				modifier += resourcesToAlphaMask(data.neededResources[i]) + ":";
+				modifier += resourcesToAlphaMask(neededResources) + ":";
 			}
 			else
 				button.enabled = controlsPlayer(data.unitEntState.player);
 
-			if (data.template[i].icon)
-				data.icon[i].sprite = modifier + "stretched:session/portraits/" + data.template[i].icon;
+			if (template.icon)
+				icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
+
+			// Position
+			setPanelObjectPosition(button, position, data.rowLength);
+
+			// prepare to handle the top button (if any)
+			position -= data.rowLength;
 		}
-		for (let button of data.buttonsToHide)
-			button.hidden = true;
-		// show the tech connector
-		data.pair.hidden = data.item.tech.pair == null;
-	},
-	"setPosition": function(data)
-	{
-		for (var i in data.button)
-			setPanelObjectPosition(data.button[i], data.positions[i], data.rowLength);
-		setPanelObjectPosition(data.pair, data.i, data.rowLength);
+
+
+		return true;
 	}
 };
 
@@ -946,25 +903,23 @@
 			return [];
 		return g_Selection.groups.getTemplateNames();
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
-		data.entType = data.item;
-		data.template = GetTemplateData(data.entType);
-		if (!data.template)
+		// Get Data
+		let template = GetTemplateData(data.item);
+		if (!template)
 			return false;
-		data.name = getEntityNames(data.template);
 
-		var ents = g_Selection.groups.getEntsByName(data.item);
-		data.count = ents.length;
-		for (var ent of ents)
+		let ents = g_Selection.groups.getEntsByName(data.item);
+		for (let ent of ents)
 		{
-			var state = GetEntityState(ent);
+			let state = GetEntityState(ent);
 
 			if (state.resourceCarrying && state.resourceCarrying.length !== 0)
 			{
 				if (!data.carried)
 					data.carried = {};
-				var carrying = state.resourceCarrying[0];
+				let carrying = state.resourceCarrying[0];
 				if (data.carried[carrying.type])
 					data.carried[carrying.type] += carrying.amount;
 				else
@@ -975,9 +930,9 @@
 			{
 				if (!data.carried)
 					data.carried = {};
-				var amount = state.trader.goods.amount;
-				var type = state.trader.goods.type;
-				var totalGain = amount.traderGain;
+				let amount = state.trader.goods.amount;
+				let type = state.trader.goods.type;
+				let totalGain = amount.traderGain;
 				if (amount.market1Gain)
 					totalGain += amount.market1Gain;
 				if (amount.market2Gain)
@@ -988,30 +943,29 @@
 					data.carried[type] = totalGain;
 			}
 		}
-		return true;
-	},
-	"setTooltip": function(data)
-	{
-		let tooltip = data.name;
+
+		// Tooltip
+		let tooltip = getEntityNames(template);
 		if (data.carried)
 			tooltip += "\n" + Object.keys(data.carried).map(res =>
 				getCostComponentDisplayIcon(res) + data.carried[res]
 			).join(" ");
 		data.button.tooltip = tooltip;
-	},
-	"setCountDisplay": function(data)
-	{
-		data.countDisplay.caption = data.count || "";
-	},
-	"setAction": function(data)
-	{
+
+		// Count
+		data.countDisplay.caption = ents.length || "";
+
+		// Action
 		data.button.onpressright = function() { changePrimarySelectionGroup(data.item, true); };
 		data.button.onpress = function() { changePrimarySelectionGroup(data.item, false); };
-	},
-	"setGraphics": function(data)
-	{
-		if (data.template.icon)
-			data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
+
+		// Graphics
+		if (template.icon)
+			data.icon.sprite = "stretched:session/portraits/" + template.icon;
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
+		return true;
 	}
 };
 
@@ -1027,27 +981,25 @@
 			return [];
 		return unitEntState.unitAI.possibleStances;
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
-		data.stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
+		// Action
+		data.button.onPress = function() { performStance(data.unitEntState, data.item); };
+
+		// Tooltip
+		data.button.tooltip = getStanceDisplayName(data.item) + "\n[font=\"sans-13\"]" + getStanceTooltip(data.item) + "[/font]";
+
+		// Graphics
+		data.guiSelection.hidden = !Engine.GuiInterfaceCall("IsStanceSelected", {
 			"ents": data.selection,
 			"stance": data.item
 		});
+		data.icon.sprite = "stretched:session/icons/stances/" + data.item + ".png";
+		data.button.enabled = controlsPlayer(data.unitEntState.player);
+
+		// Position
+		setPanelObjectPosition(data.button, data.i, data.rowLength);
 		return true;
-	},
-	"setAction": function(data)
-	{
-		data.button.onPress = function() { performStance(data.unitEntState, data.item); };
-	},
-	"setTooltip": function(data)
-	{
-		data.button.tooltip = getStanceDisplayName(data.item) + "\n[font=\"sans-13\"]" + getStanceTooltip(data.item) + "[/font]";
-	},
-	"setGraphics": function(data)
-	{
-		data.guiSelection.hidden = !data.stanceSelected;
-		data.icon.sprite = "stretched:session/icons/stances/"+data.item+".png";
-		data.button.enabled = controlsPlayer(data.unitEntState.player);
 	}
 };
 
@@ -1061,92 +1013,101 @@
 	{
 		return getAllTrainableEntitiesFromSelection();
 	},
-	"addData": function(data)
+	"setupButton": function(data)
 	{
-		data.entType = data.item;
-		data.template = GetTemplateData(data.entType);
-		if (!data.template)
+		// Get Data
+		let template = GetTemplateData(data.item);
+		if (!template)
 			return false;
 
-		data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
-			"tech": data.template.requiredTechnology,
+		let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
+			"tech": template.requiredTechnology,
 			"player": data.unitEntState.player
 		});
 
-		var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
-			getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.entType, data.selection);
+		let [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
+			getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.item, data.selection);
 
-		data.buildingsCountToTrainFullBatch = buildingsCountToTrainFullBatch;
-		data.fullBatchSize = fullBatchSize;
-		data.remainderBatch = remainderBatch;
-
-		data.trainNum = buildingsCountToTrainFullBatch || 1; // train at least one unit
+		let trainNum = buildingsCountToTrainFullBatch || 1; // train at least one unit
 		if (Engine.HotkeyIsPressed("session.batchtrain"))
-			data.trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
+			trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
 
-		if (data.template.cost)
-			data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
-				"cost": multiplyEntityCosts(data.template, data.trainNum),
+		let neededResources;
+		if (template.cost)
+			neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
+				"cost": multiplyEntityCosts(template, trainNum),
 				"player": data.unitEntState.player
 			});
 
-		return true;
-	},
-	"setAction": function(data)
-	{
+		// Action
 		data.button.onPress = function() { addTrainingToQueue(data.selection, data.item, data.playerState); };
-	},
-	"setCountDisplay": function(data)
-	{
-		data.countDisplay.caption = data.trainNum > 1 ? data.trainNum : "";
-	},
-	"setTooltip": function(data)
-	{
-		var tooltip = "";
-		var key = Engine.ConfigDB_GetValue("user", "hotkey.session.queueunit." + (data.i + 1));
+
+		// Count
+		data.countDisplay.caption = trainNum > 1 ? trainNum : "";
+
+		// Tooltip
+		let tooltip = "";
+		let key = Engine.ConfigDB_GetValue("user", "hotkey.session.queueunit." + (data.i + 1));
 		if (key)
 			tooltip += "[color=\"255 251 131\"][font=\"sans-bold-16\"]\\[" + key + "][/font][/color] ";
 
-		tooltip += getEntityNamesFormatted(data.template);
-		tooltip += getVisibleEntityClassesFormatted(data.template);
-		tooltip += getAurasTooltip(data.template);
+		tooltip += getEntityNamesFormatted(template);
+		tooltip += getVisibleEntityClassesFormatted(template);
+		tooltip += getAurasTooltip(template);
 
-		if (data.template.tooltip)
-			tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
+		if (template.tooltip)
+			tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
 
-		tooltip += "\n" + getEntityCostTooltip(data.template, data.trainNum, data.unitEntState.id);
+		tooltip += "\n" + getEntityCostTooltip(template, trainNum, data.unitEntState.id);
 
-		data.limits = getEntityLimitAndCount(data.playerState, data.entType);
+		let limits = getEntityLimitAndCount(data.playerState, data.item);
 
-		tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
+		tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
 		if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true")
 		{
-			if (data.template.health)
-				tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + data.template.health;
-			if (data.template.attack)
-				tooltip += "\n" + getAttackTooltip(data.template);
-			if (data.template.armour)
-				tooltip += "\n" + getArmorTooltip(data.template.armour);
-			if (data.template.speed)
-				tooltip += "\n" + getSpeedTooltip(data.template);
+			if (template.health)
+				tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + template.health;
+			if (template.attack)
+				tooltip += "\n" + getAttackTooltip(template);
+			if (template.armour)
+				tooltip += "\n" + getArmorTooltip(template.armour);
+			if (template.speed)
+				tooltip += "\n" + getSpeedTooltip(template);
 		}
-		tooltip += "[color=\"255 251 131\"]" + formatBatchTrainingString(data.buildingsCountToTrainFullBatch, data.fullBatchSize, data.remainderBatch) + "[/color]";
-		if (!data.technologyEnabled)
+		tooltip += "[color=\"255 251 131\"]" + formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) + "[/color]";
+		if (!technologyEnabled)
 		{
-			var techName = getEntityNames(GetTechnologyData(data.template.requiredTechnology));
+			let techName = getEntityNames(GetTechnologyData(template.requiredTechnology));
 			tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { "technology": techName });
 		}
-		if (data.neededResources)
-			tooltip += getNeededResourcesTooltip(data.neededResources);
+		if (neededResources)
+			tooltip += getNeededResourcesTooltip(neededResources);
 
 		data.button.tooltip = tooltip;
-	},
-	// disable and enable buttons in the same way as when you do for the construction
-	"setGraphics": g_SelectionPanels.Construction.setGraphics,
-	"setPosition": function(data)
-	{
-		var index = data.i + getNumberOfRightPanelButtons();
+
+		// Graphics
+		let modifier = "";
+		if (!technologyEnabled || limits.canBeAddedCount == 0)
+		{
+			data.button.enabled = false;
+			modifier += "color: 0 0 0 127:";
+			modifier += "grayscale:";
+		}
+		else if (neededResources)
+		{
+			data.button.enabled = false;
+			modifier += resourcesToAlphaMask(neededResources) +":";
+		}
+		else
+			data.button.enabled = controlsPlayer(data.unitEntState.player);
+
+		if (template.icon)
+			data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
+
+		// Position
+		let index = data.i + getNumberOfRightPanelButtons();
 		setPanelObjectPosition(data.button, index, data.rowLength);
+		return true;
 	}
 };
 
@@ -1158,7 +1119,7 @@
  *
  * Note that the panel needs to appear in the list to get rendered.
  */
-var g_PanelsOrder = [
+let g_PanelsOrder = [
 	// LEFT PANE
 	"Barter", // must always be visible on markets
 	"Garrison", // more important than Formation, as you want to see the garrisoned units in ships
Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js	(revision 18243)
+++ binaries/data/mods/public/gui/session/unit_commands.js	(working copy)
@@ -61,13 +61,11 @@
 	// Make buttons
 	for (let i = 0; i < numberOfItems; ++i)
 	{
-		var item = items[i];
-
 		// STANDARD DATA
 		// add standard data
-		var data = {
+		let data = {
 			"i": i,
-			"item": item,
+			"item": items[i],
 			"selection": selection,
 			"playerState": playerState,
 			"unitEntState": unitEntState,
@@ -92,27 +90,11 @@
 			data.button.caption = "";
 		}
 
-		// GENERAL DATA
-		// add general data, and a chance to abort on faulty data
-		if (g_SelectionPanels[guiName].addData)
-		{
-			var success = g_SelectionPanels[guiName].addData(data);
-			if (!success)
-				continue; // ignore faulty data
-		}
-
 		// SET CONTENT
-		// run all content setters
-		for (var f in g_SelectionPanels[guiName])
-		{
-			if (f.match(/^set/))
-				g_SelectionPanels[guiName][f](data);
-		}
+		if (g_SelectionPanels[guiName].setupButton)
+			if (!g_SelectionPanels[guiName].setupButton(data))
+				continue;
 
-		// Special case: position
-		if (!g_SelectionPanels[guiName].setPosition)
-			setPanelObjectPosition(data.button, i, rowLength);
-
 		// TODO: we should require all entities to have icons, so this case never occurs
 		if (data.icon && !data.icon.sprite)
 			data.icon.sprite = "bkFillBlack";
