3 Commits

Author SHA1 Message Date
ede3ce1f0b Fix several bugs and refacto
Some checks failed
Continuous Deployment / lint (push) Successful in 30s
Continuous Deployment / deploy-chrome (push) Failing after 20s
Continuous Deployment / deploy-firefox (push) Successful in 3m9s
2025-11-28 19:11:21 +01:00
5b075f057e Fix market target bug 2025-11-28 10:30:53 +01:00
8d8ccd8e9d Add player id in ranking and ally members
Some checks failed
Continuous Deployment / lint (push) Successful in 30s
Continuous Deployment / deploy-chrome (push) Failing after 21s
Continuous Deployment / deploy-firefox (push) Successful in 4m38s
2025-11-24 19:04:59 +01:00
4 changed files with 453 additions and 285 deletions

View File

@@ -1,5 +1,17 @@
# Changelog # Changelog
## 1.11.2 (2026-11-28)
- correction du bug sur les multiples attaques avec comte
- correction du bug sur le bouton pour sélectionner toutes les troupes
- correction du bug sur l'envoi de ressources vers un village cible
- ajout des ids dans la liste des joueurs d'une alliance
- ajout de l'id sur le profil d'un joueur
## 1.11.1 (2026-11-24)
- ajout de l'id des joueurs dans le classement général et la liste des membres de l'alliance
## 1.10.1 (2026-11-20) ## 1.10.1 (2026-11-20)
- envoi des ressources par milliers sur le marché - envoi des ressources par milliers sur le marché

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "KAplus", "name": "KAplus",
"version": "1.10.1", "version": "1.11.2",
"developer": { "developer": {
"name": "Samuel Campos", "name": "Samuel Campos",

View File

@@ -5,8 +5,8 @@
"firefox" "firefox"
], ],
"release_notes": { "release_notes": {
"fr": "- envoi des ressources par milliers sur le marché", "fr": "- correction du bug sur les multiples attaques avec comte\n- correction du bug sur le bouton pour sélectionner toutes les troupes\n- correction du bug sur l'envoi de ressources vers un village cible\n- ajout des ids dans la liste des joueurs d'une alliance\n- ajout de l'id sur le profil d'un joueur",
"en-US": "- send resources by thousands in market" "en-US": "- fix bug on multiple attacks with snob\n- fix bug on button for selecting all units\n- fix bug on sending resources to a target village\n- add player id in other ally member list\n- add id on player profile"
} }
} }
} }

View File

@@ -27,9 +27,11 @@ function createCustomElement(tag, attrs, text, style) {
let elt = document.createElement(tag); let elt = document.createElement(tag);
if (attrs) { if (attrs) {
for (let [key, value] of Object.entries(attrs)) { for (let [key, value] of Object.entries(attrs)) {
if (value !== null) {
elt.setAttribute(key, value.toString()); elt.setAttribute(key, value.toString());
} }
} }
}
if (text) { if (text) {
elt.textContent = text.toString(); elt.textContent = text.toString();
} }
@@ -100,6 +102,23 @@ function shortcutElementReplace(elt, img, text) {
} }
} }
function customizeNavbar(layCastleElement) {
/* Improve navbar icons */
let shortcutElements = layCastleElement.getElementsByClassName("shortcut_element");
shortcutElementReplace(shortcutElements[0], "ranking", shortcutElements[0].textContent.replace(/[^0-9]/g, ""));
shortcutElementReplace(shortcutElements[1], "ally", "");
shortcutElementReplace(shortcutElements[2], "profile", "");
shortcutElementReplace(shortcutElements[3], "premium", "");
shortcutElementReplace(shortcutElements[4], "messages", "");
shortcutElementReplace(shortcutElements[5], "tools", "");
shortcutElementReplace(shortcutElements[6], "favorites", "");
/* Center navbar */
let shortcutContainers = layCastleElement.getElementsByClassName("shortcut_container");
shortcutContainers[0].classList.add("shortcut_container_left");
shortcutContainers[2].classList.add("shortcut_container_right");
}
function countUpMs() { function countUpMs() {
let arrivalDate = new Date(new Date().getTime() + movingDuration * 1000); let arrivalDate = new Date(new Date().getTime() + movingDuration * 1000);
let prefix = ""; let prefix = "";
@@ -119,56 +138,48 @@ function countUpMs() {
+ Math.floor(arrivalDate.getMilliseconds() / 100); + Math.floor(arrivalDate.getMilliseconds() / 100);
} }
function handleDomContentLoaded() { function removeAdsBanner() {
/* Remove iframe banner */ /* Remove iframe banner */
document.getElementById("banner_skyscraper").remove(); document.getElementById("banner_skyscraper").remove();
} }
function main() { function showPlayersId() {
/* Exit immediately if extension has already been loaded */ /* Add a column with player id */
let kaplus = document.getElementById("kaplus-marker"); let mainContentPane = document.getElementsByClassName("contentpane")[1];
if (kaplus) { let borderListTable = mainContentPane.getElementsByClassName("borderlist")[0];
let playerRows = borderListTable.getElementsByTagName("tr");
let headerCells = playerRows[0].getElementsByTagName("th");
let nameCellIndex = -1;
for (let i = 0; i < headerCells.length; i ++) {
if (headerCells[i].textContent === "Nom") {
nameCellIndex = i;
break;
}
}
if (nameCellIndex === -1) {
/* Name column not found, so return */
console.log("Column 'Nom' not found, cannot show player ids :(");
return; return;
} }
/* Exit immediately if not on game page */ let idHeaderCell = createCustomElement("th", {"class": headerCells[0].getAttribute("class")}, "Id");
let layCastleTopElements = document.getElementsByClassName("lay_castle_top"); for (let i = 1; i < playerRows.length; i ++) {
if (layCastleTopElements.length === 0) { let playerCells = playerRows[i].getElementsByTagName("td");
return; let playerProfileLink = playerCells[nameCellIndex].getElementsByTagName("a")[0].getAttribute("href");
let idValue = playerProfileLink.replace(/^.*id=(\d+)$/, "$1");
let idCell = createCustomElement("td", {"class": playerCells[0].getAttribute("class")}, idValue);
playerRows[i].insertBefore(idCell, playerCells[nameCellIndex]);
}
playerRows[0].insertBefore(idHeaderCell, headerCells[nameCellIndex]);
} }
window.addEventListener("DOMContentLoaded", handleDomContentLoaded);
/* Add extension marker */ function showPlayerUnitPointsAndId() {
document.body.appendChild(createCustomElement("div", {"id": "kaplus-marker"}, null, {"display": "none"}));
/* Improve navbar icons */
let shortcutElements = layCastleTopElements[0].getElementsByClassName("shortcut_element");
shortcutElementReplace(shortcutElements[0], "ranking", shortcutElements[0].textContent.replace(/[^0-9]/g, ""));
shortcutElementReplace(shortcutElements[1], "ally", "");
shortcutElementReplace(shortcutElements[2], "profile", "");
shortcutElementReplace(shortcutElements[3], "premium", "");
shortcutElementReplace(shortcutElements[4], "messages", "");
shortcutElementReplace(shortcutElements[5], "tools", "");
shortcutElementReplace(shortcutElements[6], "favorites", "");
/* Center navbar */
let shortcutContainers = layCastleTopElements[0].getElementsByClassName("shortcut_container");
shortcutContainers[0].classList.add("shortcut_container_left");
shortcutContainers[2].classList.add("shortcut_container_right");
/* Parse url params and switch case */
let urlParams = new URLSearchParams(window.location.search);
let section = urlParams.get("s");
let module = urlParams.get("m");
let sub = urlParams.get("sub");
let sendCommandForm = document.getElementById("sendCommandForm");
/* Display unit-points on user profile */
if (section === "info_player" && (module === "profile" || module === null)) {
let mainContentPane = document.getElementsByClassName("contentpane")[1]; let mainContentPane = document.getElementsByClassName("contentpane")[1];
let borderLists = mainContentPane.getElementsByClassName("borderlist"); let borderLists = mainContentPane.getElementsByClassName("borderlist");
let playerPropertiesTable = borderLists[0]; let playerPropertiesTable = borderLists[0];
let playerPropertiesTbody = playerPropertiesTable.getElementsByTagName("tbody")[0];
let playerPropertiesRows = playerPropertiesTable.getElementsByTagName("tr"); let playerPropertiesRows = playerPropertiesTable.getElementsByTagName("tr");
let totalPoints = num(playerPropertiesRows[2].getElementsByTagName("td")[1].textContent); let totalPoints = num(playerPropertiesRows[2].getElementsByTagName("td")[1].textContent);
let villagesCount = num(playerPropertiesRows[4].getElementsByTagName("td")[1].textContent); let villagesCount = num(playerPropertiesRows[4].getElementsByTagName("td")[1].textContent);
@@ -187,13 +198,22 @@ function main() {
armyPercent = (Math.round(armyPoints / villagesCount) / 100).toString() + " %"; armyPercent = (Math.round(armyPoints / villagesCount) / 100).toString() + " %";
} }
playerPropertiesTable.appendChild(createKeyValueRow("Points troupes:", str(armyPoints))); let moduleMenu = mainContentPane.getElementsByTagName("table")[0];
playerPropertiesTable.appendChild(createKeyValueRow("% points troupes:", armyPercent)); let moduleHyperlink = moduleMenu.getElementsByTagName("a")[0];
let playerId = moduleHyperlink.getAttribute("href").replace(/^.*[?&]id=(\d+).*$/, "$1");
playerPropertiesTbody.appendChild(createKeyValueRow("Points troupes:", str(armyPoints)));
playerPropertiesTbody.appendChild(createKeyValueRow("% points troupes:", armyPercent));
playerPropertiesTbody.insertBefore(createKeyValueRow("Id:", playerId), playerPropertiesRows[2]);
} }
/* Display unit-points on village overview */ function showVillageUnitPoints() {
if (section === "overview") { let settlementElt = document.getElementById("settlement");
let settlements = document.getElementById("settlement").textContent.split("|"); if (settlementElt === null) {
console.log("Settlement element not found => cannot show village points :(");
return;
}
let settlements = settlementElt.textContent.split("|");
let noBorderRows = document.getElementsByClassName("noborder"); let noBorderRows = document.getElementsByClassName("noborder");
let villagePointsRow = noBorderRows[0]; let villagePointsRow = noBorderRows[0];
for (let i = 0; i < noBorderRows.length; i++) { for (let i = 0; i < noBorderRows.length; i++) {
@@ -208,31 +228,69 @@ function main() {
villagePointsRow.after(unitPointsRow); villagePointsRow.after(unitPointsRow);
} }
/* Units order page */ function showBarrackSelectAllUnits() {
if (section === "build_barracks" && (module === null || module === "command") && sendCommandForm !== null) { let sendCommandForm = document.getElementById("sendCommandForm");
let barracksCommands = sendCommandForm.getElementsByClassName("barracksCommand"); let barracksCommands = sendCommandForm.getElementsByClassName("barracksCommand");
let borderListTables = sendCommandForm.getElementsByClassName("borderlist"); let borderListTables = sendCommandForm.getElementsByClassName("borderlist");
let quantityLabel = createCustomElement("label"); let unitCountBoxes = [];
let quantityInput = createCustomElement("input", {"type": "checkbox"});
quantityInput.addEventListener("change", function () {
let clickSpans = [];
if (barracksCommands.length === 1) { if (barracksCommands.length === 1) {
clickSpans = barracksCommands[0].getElementsByClassName("click"); unitCountBoxes = barracksCommands[0].getElementsByClassName("box");
} else if (borderListTables.length === 1) { } else if (borderListTables.length === 1) {
clickSpans = borderListTables[0].getElementsByClassName("click"); unitCountBoxes = borderListTables[0].getElementsByTagName("td");
} }
for (let i = 0; i < 12; i++) { for (let i = 0; i < unitCountBoxes.length; i ++) {
if (clickSpans[i].classList.contains("all")) { let unitCountInputs = unitCountBoxes[i].getElementsByTagName("input");
if (unitCountInputs.length === 0) {
continue; continue;
} }
clickSpans[i].click(); let unitCountInput = unitCountInputs[0];
if (unitCountInput.getAttribute("type") === null) {
unitCountInput.setAttribute("type", "number");
unitCountInput.style.width = "65px";
}
}
let sendXInput = document.getElementById("send_x");
sendXInput.setAttribute("type", "number");
if (sendXInput.value === "0") {
sendXInput.value = "";
}
let sendYInput = document.getElementById("send_y");
sendYInput.setAttribute("type", "number");
if (sendYInput.value === "0") {
sendYInput.value = "";
}
let selectAllLabel = createCustomElement("label");
let selectAllInput = createCustomElement("input", {"type": "checkbox"});
selectAllInput.addEventListener("change", function () {
for (let i = 0; i < unitCountBoxes.length; i ++) {
let unitCountBox = unitCountBoxes[i];
let unitCountClicks = unitCountBox.getElementsByClassName("click");
if (unitCountClicks.length === 0) {
continue;
}
let unitCountClick = unitCountClicks[0];
if (unitCountClick.classList.contains("all")) {
continue;
}
let unitCountInput = unitCountBox.getElementsByTagName("input")[0];
if (this.checked && unitCountClick.textContent !== "(0)") {
unitCountInput.value = unitCountClick.textContent.replace(/^\(([^.]*)(\.([^.]*))?\)$/, "$1$3");
} else {
unitCountInput.value = "";
}
} }
}) })
quantityLabel.appendChild(quantityInput); selectAllLabel.appendChild(selectAllInput);
let quantitySpan = createCustomElement("span", {"class": "click all"}, "(Tout sélectionner)"); let selectAllSpan = createCustomElement("span", {"class": "click all"}, "(Tout sélectionner)");
quantityLabel.appendChild(quantitySpan); selectAllLabel.appendChild(selectAllSpan);
/* Modern style */
if (barracksCommands.length === 1) { if (barracksCommands.length === 1) {
let boxCell = createCustomElement("div", {"class": "box"}); let boxCell = createCustomElement("div", {"class": "box"});
@@ -257,16 +315,17 @@ function main() {
nameCell.appendChild(nameA); nameCell.appendChild(nameA);
boxCell.appendChild(nameCell); boxCell.appendChild(nameCell);
let quantityCell = createCustomElement("div", {"class": "quantity"}); let selectAllCell = createCustomElement("div", {"class": "quantity"});
quantityCell.appendChild(quantityLabel); selectAllCell.appendChild(selectAllLabel);
boxCell.appendChild(quantityCell); boxCell.appendChild(selectAllCell);
let brTag = barracksCommands[0].getElementsByTagName("br")[1]; let brTag = barracksCommands[0].getElementsByTagName("br")[1];
barracksCommands[0].insertBefore(boxCell, brTag); barracksCommands[0].insertBefore(boxCell, brTag);
/* Classic style */
} else if (borderListTables.length === 1) { } else if (borderListTables.length === 1) {
let borderListRows = borderListTables[0].getElementsByTagName("tr"); let borderListRows = borderListTables[0].getElementsByTagName("tr");
let selectAllCell = borderListRows[1].getElementsByTagName("td")[3]; let selectAllCell = borderListRows[4].getElementsByTagName("td")[3];
let imageA = createCustomElement("a", {"href": "help.php?m=units", "target": "_help"}); let imageA = createCustomElement("a", {"href": "help.php?m=units", "target": "_help"});
let imageImg = createCustomElement( let imageImg = createCustomElement(
@@ -275,13 +334,11 @@ function main() {
imageA.appendChild(imageImg); imageA.appendChild(imageImg);
selectAllCell.appendChild(imageA); selectAllCell.appendChild(imageA);
selectAllCell.appendChild(quantityLabel); selectAllCell.appendChild(selectAllLabel);
} }
} }
/* Units sending page */ function showCountupTimeDecimals() {
if (section === "build_barracks" && module === "command" && sub === "send" && sendCommandForm === null) {
/* Improve countup time cell */
let oldCell = document.getElementById("countup-time"); let oldCell = document.getElementById("countup-time");
let hms = oldCell.parentElement.previousElementSibling.getElementsByTagName("td")[1].textContent.split(":"); let hms = oldCell.parentElement.previousElementSibling.getElementsByTagName("td")[1].textContent.split(":");
movingDuration = parseInt(hms[0]) * 3600 + parseInt(hms[1]) * 60 + parseInt(hms[2]) ; movingDuration = parseInt(hms[0]) * 3600 + parseInt(hms[1]) * 60 + parseInt(hms[2]) ;
@@ -293,8 +350,9 @@ function main() {
oldCell.parentElement.parentElement.insertBefore(newRow, oldCell.parentElement); oldCell.parentElement.parentElement.insertBefore(newRow, oldCell.parentElement);
oldCell.parentElement.style.display = "none"; oldCell.parentElement.style.display = "none";
setInterval(countUpMs, 100); setInterval(countUpMs, 100);
}
/* Allow multiple occurrences of send */ function showOccurrencesInput() {
let table = createCustomElement("table", {"class": "borderlist"}); let table = createCustomElement("table", {"class": "borderlist"});
let tbody = createCustomElement("tbody"); let tbody = createCustomElement("tbody");
let tr = createCustomElement("tr"); let tr = createCustomElement("tr");
@@ -310,27 +368,28 @@ function main() {
let firstInput = form.getElementsByTagName("input")[0]; let firstInput = form.getElementsByTagName("input")[0];
form.insertBefore(table, firstInput); form.insertBefore(table, firstInput);
form.addEventListener("submit", function (event) { form.addEventListener("submit", function (event) {
event.preventDefault();
let formData = new FormData(this); let formData = new FormData(this);
let attackCount = parseInt(formData.get("occurrences").toString()); let attackCount = parseInt(formData.get("occurrences").toString());
formData.delete("occurrences"); formData.delete("occurrences");
let sent = 0; let sent = 0;
for (let i = 0; i < attackCount; i ++) { for (let i = 0; i < attackCount; i ++) {
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.onload = function () { xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
sent ++; sent ++;
}
if (sent === attackCount) { if (sent === attackCount) {
window.location.replace(xhr.responseURL); window.location.replace(xhr.responseURL);
} }
} })
xhr.open(this.method, this.action, true); xhr.open(this.method, this.action, true);
xhr.send(formData); xhr.send(formData);
} }
}); });
} }
/* Market sending page */ function fixSelectVillageBug() {
if (section === "build_market" && (module === "send" || module === null)) {
/* Fix select bug in target village select */
let select = document.getElementsByName("village_name")[0]; let select = document.getElementsByName("village_name")[0];
select.removeAttribute("onchange"); select.removeAttribute("onchange");
select.addEventListener("change", function () { select.addEventListener("change", function () {
@@ -345,8 +404,9 @@ function main() {
sendY.value = xy[1]; sendY.value = xy[1];
} }
}); });
}
/* Add form to send resources by thousands */ function showThousandInputs() {
let sendForm = document.getElementsByTagName("form")[0] let sendForm = document.getElementsByTagName("form")[0]
sendForm.addEventListener("submit", function () { sendForm.addEventListener("submit", function () {
let inputs = this.getElementsByTagName("input"); let inputs = this.getElementsByTagName("input");
@@ -380,20 +440,24 @@ function main() {
} }
sendForm.insertBefore(newTable, sendFormTables[0]); sendForm.insertBefore(newTable, sendFormTables[0]);
sendForm.insertBefore(createCustomElement("br"), sendFormTables[1]); sendForm.insertBefore(createCustomElement("br"), sendFormTables[1]);
}
/* Set inputs type=number */ function fixSendResourcesInputsTypes() {
let sendInputs = sendForm.getElementsByTagName("input"); let contentPane = document.getElementsByClassName("contentpane")[1]
let sendCommandForm = contentPane.getElementsByTagName("form")[0];
let sendInputs = sendCommandForm.getElementsByTagName("input");
for (let i = 0; i < sendInputs.length; i ++) { for (let i = 0; i < sendInputs.length; i ++) {
if (sendInputs[i].name.startsWith("send_")) { if (sendInputs[i].name.startsWith("send_")) {
sendInputs[i].setAttribute("type", "number"); sendInputs[i].setAttribute("type", "number");
if (sendInputs[i].value === "0") {
sendInputs[i].value = ""; sendInputs[i].value = "";
}
sendInputs[i].style.width = "65px"; sendInputs[i].style.width = "65px";
} }
} }
} }
/* Improve attacks display */ function showSecondsAndCalculator() {
if (section === "ally" && module === "attacks") {
let serverTime = parseInt(document.getElementById("servertime").getAttribute("time")); let serverTime = parseInt(document.getElementById("servertime").getAttribute("time"));
let contentPane = document.getElementsByClassName("contentpane")[1] let contentPane = document.getElementsByClassName("contentpane")[1]
let table = contentPane.getElementsByClassName("borderlist")[0]; let table = contentPane.getElementsByClassName("borderlist")[0];
@@ -434,6 +498,98 @@ function main() {
rows[i].appendChild(calculatorCell); rows[i].appendChild(calculatorCell);
} }
} }
function main() {
/* Exit immediately if not on game page */
let layCastleTopElements = document.getElementsByClassName("lay_castle_top");
if (layCastleTopElements.length === 0) {
return;
}
/* Exit immediately if extension has already been loaded */
let kaplus = document.getElementById("kaplus-marker");
if (kaplus) {
return;
}
/* Set marker to prevent loading extension several times */
document.body.appendChild(createCustomElement("div", {"id": "kaplus-marker"}, null, {"display": "none"}));
/* Remove ads banner on dom content loaded */
window.addEventListener("DOMContentLoaded", removeAdsBanner);
/* Customize navbar */
customizeNavbar(layCastleTopElements[0]); // test: OK
/* Parse url params and switch case */
let urlParams = new URLSearchParams(window.location.search);
let section = urlParams.get("s");
let module = urlParams.get("m");
let sub = urlParams.get("sub");
/* Choose action according to section, module and sub */
switch (section) {
case "ranking":
switch (module) {
case "player": case null:
showPlayersId(); // test: OK
break;
}
break;
case "ally":
switch (module) {
case "members":
showPlayersId(); // test: OK
break;
case "attacks":
showSecondsAndCalculator(); // test: OK
break;
}
break;
case "overview":
showVillageUnitPoints(); // test: FAILED because settlement element has been removed
break;
case "build_barracks":
switch (module) {
case "command": case null:
switch (sub) {
case null:
showBarrackSelectAllUnits(); // test: OK
break;
case "send":
showCountupTimeDecimals(); // test: OK
showOccurrencesInput(); // test: OK
break;
}
break;
}
break;
case "build_market":
switch (module) {
case "send": case null:
fixSelectVillageBug(); // test: OK
showThousandInputs(); // test: OK
fixSendResourcesInputsTypes(); // test: OK
break;
}
break;
case "info_player":
switch (module) {
case "profile": case null:
showPlayerUnitPointsAndId(); // test: OK
break;
}
break;
case "info_member":
showPlayersId(); // test: OK
break;
}
} }
main(); main();