forked from OpenWF/SpaceNinjaServer
		
	feat(webui): add Mods tab with "Add Riven" option (#234)
This commit is contained in:
		
							parent
							
								
									2e8c94d34b
								
							
						
					
					
						commit
						8eb11007a7
					
				@ -2,6 +2,7 @@ import express from "express";
 | 
				
			|||||||
import path from "path";
 | 
					import path from "path";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const webuiRouter = express.Router();
 | 
					const webuiRouter = express.Router();
 | 
				
			||||||
 | 
					const rootDir = path.join(__dirname, "../..");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Redirect / to /webui/
 | 
					// Redirect / to /webui/
 | 
				
			||||||
webuiRouter.get("/", (_req, res) => {
 | 
					webuiRouter.get("/", (_req, res) => {
 | 
				
			||||||
@ -16,7 +17,15 @@ webuiRouter.use("/webui", (req, res, next) => {
 | 
				
			|||||||
    next();
 | 
					    next();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Serve static files from the webui directory
 | 
					// Serve virtual routes
 | 
				
			||||||
webuiRouter.use("/webui", express.static(path.join(__dirname, "../..", "static/webui")));
 | 
					webuiRouter.get("/webui/inventory", (_req, res) => {
 | 
				
			||||||
 | 
					    res.sendFile(path.join(rootDir, "static/webui/index.html"));
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					webuiRouter.get("/webui/mods", (_req, res) => {
 | 
				
			||||||
 | 
					    res.sendFile(path.join(rootDir, "static/webui/index.html"));
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Serve static files
 | 
				
			||||||
 | 
					webuiRouter.use("/webui", express.static(path.join(rootDir, "static/webui")));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { webuiRouter };
 | 
					export { webuiRouter };
 | 
				
			||||||
 | 
				
			|||||||
@ -2,17 +2,66 @@
 | 
				
			|||||||
<html lang="en" data-bs-theme="dark">
 | 
					<html lang="en" data-bs-theme="dark">
 | 
				
			||||||
    <head>
 | 
					    <head>
 | 
				
			||||||
        <title>OpenWF WebUI</title>
 | 
					        <title>OpenWF WebUI</title>
 | 
				
			||||||
 | 
					        <meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
				
			||||||
        <link
 | 
					        <link
 | 
				
			||||||
            href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
 | 
					            href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
 | 
				
			||||||
            rel="stylesheet"
 | 
					            rel="stylesheet"
 | 
				
			||||||
            integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
 | 
					            integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
 | 
				
			||||||
            crossorigin="anonymous"
 | 
					            crossorigin="anonymous"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					        <link rel="stylesheet" href="style.css" />
 | 
				
			||||||
    </head>
 | 
					    </head>
 | 
				
			||||||
    <body>
 | 
					    <body>
 | 
				
			||||||
        <div class="container pt-3 pb-3">
 | 
					        <nav class="navbar sticky-top bg-body-tertiary">
 | 
				
			||||||
            <h1>OpenWF WebUI</h1>
 | 
					            <div class="container">
 | 
				
			||||||
            <div id="login-view">
 | 
					                <button
 | 
				
			||||||
 | 
					                    class="navbar-toggler d-lg-none"
 | 
				
			||||||
 | 
					                    type="button"
 | 
				
			||||||
 | 
					                    data-bs-toggle="offcanvas"
 | 
				
			||||||
 | 
					                    data-bs-target="#sidebar"
 | 
				
			||||||
 | 
					                    aria-controls="sidebar"
 | 
				
			||||||
 | 
					                    aria-label="Toggle sidebar"
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    <span class="navbar-toggler-icon"></span>
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					                <a class="navbar-brand">OpenWF WebUI</a>
 | 
				
			||||||
 | 
					                <div class="nav-item dropdown">
 | 
				
			||||||
 | 
					                    <button
 | 
				
			||||||
 | 
					                        class="nav-link dropdown-toggle displayname"
 | 
				
			||||||
 | 
					                        data-bs-toggle="dropdown"
 | 
				
			||||||
 | 
					                        aria-expanded="false"
 | 
				
			||||||
 | 
					                    ></button>
 | 
				
			||||||
 | 
					                    <ul class="dropdown-menu dropdown-menu-end">
 | 
				
			||||||
 | 
					                        <li><a class="dropdown-item" href="/webui/" onclick="logout();">Logout</a></li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </nav>
 | 
				
			||||||
 | 
					        <div class="container pt-3 pb-3" id="main-view">
 | 
				
			||||||
 | 
					            <div class="offcanvas-lg offcanvas-start" tabindex="-1" id="sidebar" aria-labelledby="sidebarLabel">
 | 
				
			||||||
 | 
					                <div class="offcanvas-header">
 | 
				
			||||||
 | 
					                    <h5 class="offcanvas-title" id="sidebarLabel">Sidebar</h5>
 | 
				
			||||||
 | 
					                    <button
 | 
				
			||||||
 | 
					                        type="button"
 | 
				
			||||||
 | 
					                        class="btn-close"
 | 
				
			||||||
 | 
					                        data-bs-dismiss="offcanvas"
 | 
				
			||||||
 | 
					                        data-bs-target="#sidebar"
 | 
				
			||||||
 | 
					                        aria-label="Close"
 | 
				
			||||||
 | 
					                    ></button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="offcanvas-body">
 | 
				
			||||||
 | 
					                    <ul>
 | 
				
			||||||
 | 
					                        <li><a href="/webui/inventory">Inventory</a></li>
 | 
				
			||||||
 | 
					                        <li><a href="/webui/mods">Mods</a></li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					                <p id="refresh-note" class="mb-4">
 | 
				
			||||||
 | 
					                    Note: Changes made here will only be reflected in-game when the game re-downloads your inventory.
 | 
				
			||||||
 | 
					                    Visiting the navigation should be the easiest way to trigger that.
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					                <div data-route="/webui/">
 | 
				
			||||||
                    <p>Login using your OpenWF account credentials.</p>
 | 
					                    <p>Login using your OpenWF account credentials.</p>
 | 
				
			||||||
                    <form onsubmit="doLogin();return false;">
 | 
					                    <form onsubmit="doLogin();return false;">
 | 
				
			||||||
                        <label for="email">Email address</label>
 | 
					                        <label for="email">Email address</label>
 | 
				
			||||||
@ -24,12 +73,7 @@
 | 
				
			|||||||
                        <button class="btn btn-primary" type="submit">Login</button>
 | 
					                        <button class="btn btn-primary" type="submit">Login</button>
 | 
				
			||||||
                    </form>
 | 
					                    </form>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            <div id="main-view" class="d-none">
 | 
					                <div data-route="/webui/inventory">
 | 
				
			||||||
                <p>Hello, <b class="displayname"></b>! <a href="#" onclick="logout();">Logout</a></p>
 | 
					 | 
				
			||||||
                <p class="mb-4">
 | 
					 | 
				
			||||||
                    Note: Changes made here will only be reflected in-game when the game re-downloads your inventory.
 | 
					 | 
				
			||||||
                    Visiting the navigation should be the easiest way to trigger that.
 | 
					 | 
				
			||||||
                </p>
 | 
					 | 
				
			||||||
                    <div class="card mb-4">
 | 
					                    <div class="card mb-4">
 | 
				
			||||||
                        <h5 class="card-header">Add Items</h5>
 | 
					                        <h5 class="card-header">Add Items</h5>
 | 
				
			||||||
                        <form class="card-body input-group" onsubmit="doAcquireMiscItems();return false;">
 | 
					                        <form class="card-body input-group" onsubmit="doAcquireMiscItems();return false;">
 | 
				
			||||||
@ -47,7 +91,11 @@
 | 
				
			|||||||
                                        <tbody id="warframe-list"></tbody>
 | 
					                                        <tbody id="warframe-list"></tbody>
 | 
				
			||||||
                                    </table>
 | 
					                                    </table>
 | 
				
			||||||
                                    <form class="input-group" onsubmit="doAcquireWarframe();return false;">
 | 
					                                    <form class="input-group" onsubmit="doAcquireWarframe();return false;">
 | 
				
			||||||
                                    <input class="form-control" id="warframe-to-acquire" list="datalist-warframes" />
 | 
					                                        <input
 | 
				
			||||||
 | 
					                                            class="form-control"
 | 
				
			||||||
 | 
					                                            id="warframe-to-acquire"
 | 
				
			||||||
 | 
					                                            list="datalist-warframes"
 | 
				
			||||||
 | 
					                                        />
 | 
				
			||||||
                                        <button class="btn btn-primary" type="submit">Add</button>
 | 
					                                        <button class="btn btn-primary" type="submit">Add</button>
 | 
				
			||||||
                                    </form>
 | 
					                                    </form>
 | 
				
			||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
@ -69,6 +117,30 @@
 | 
				
			|||||||
                        </div>
 | 
					                        </div>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div data-route="/webui/mods">
 | 
				
			||||||
 | 
					                    <div class="card mb-4">
 | 
				
			||||||
 | 
					                        <h5 class="card-header">Add Riven</h5>
 | 
				
			||||||
 | 
					                        <form class="card-body" onsubmit="doAcquireRiven();return false;">
 | 
				
			||||||
 | 
					                            <select class="form-control mb-3" id="addriven-type">
 | 
				
			||||||
 | 
					                                <option value="LotusArchgunRandomModRare">LotusArchgunRandomModRare</option>
 | 
				
			||||||
 | 
					                                <option value="LotusModularMeleeRandomModRare">LotusModularMeleeRandomModRare</option>
 | 
				
			||||||
 | 
					                                <option value="LotusModularPistolRandomModRare">LotusModularPistolRandomModRare</option>
 | 
				
			||||||
 | 
					                                <option value="LotusPistolRandomModRare">LotusPistolRandomModRare</option>
 | 
				
			||||||
 | 
					                                <option value="LotusRifleRandomModRare" selected>LotusRifleRandomModRare</option>
 | 
				
			||||||
 | 
					                                <option value="LotusShotgunRandomModRare">LotusShotgunRandomModRare</option>
 | 
				
			||||||
 | 
					                                <option value="PlayerMeleeWeaponRandomModRare">PlayerMeleeWeaponRandomModRare</option>
 | 
				
			||||||
 | 
					                            </select>
 | 
				
			||||||
 | 
					                            <textarea
 | 
				
			||||||
 | 
					                                id="addriven-fingerprint"
 | 
				
			||||||
 | 
					                                class="form-control mb-3"
 | 
				
			||||||
 | 
					                                placeholder="Fingerprint"
 | 
				
			||||||
 | 
					                            ></textarea>
 | 
				
			||||||
 | 
					                            <button class="btn btn-primary" style="margin-right: 5px" type="submit">Add</button>
 | 
				
			||||||
 | 
					                            <a href="https://riven.builds.wf/" target="_blank">Need help with the fingerprint?</a>
 | 
				
			||||||
 | 
					                        </form>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <datalist id="datalist-warframes"></datalist>
 | 
					        <datalist id="datalist-warframes"></datalist>
 | 
				
			||||||
        <datalist id="datalist-weapons"></datalist>
 | 
					        <datalist id="datalist-weapons"></datalist>
 | 
				
			||||||
@ -79,6 +151,12 @@
 | 
				
			|||||||
            crossorigin="anonymous"
 | 
					            crossorigin="anonymous"
 | 
				
			||||||
        ></script>
 | 
					        ></script>
 | 
				
			||||||
        <script src="https://cdn.jsdelivr.net/gh/angeal185/whirlpool-js/dist/whirlpool-js.min.js"></script>
 | 
					        <script src="https://cdn.jsdelivr.net/gh/angeal185/whirlpool-js/dist/whirlpool-js.min.js"></script>
 | 
				
			||||||
 | 
					        <script src="single.js"></script>
 | 
				
			||||||
        <script src="script.js"></script>
 | 
					        <script src="script.js"></script>
 | 
				
			||||||
 | 
					        <script
 | 
				
			||||||
 | 
					            src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
 | 
				
			||||||
 | 
					            integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
 | 
				
			||||||
 | 
					            crossorigin="anonymous"
 | 
				
			||||||
 | 
					        ></script>
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
function doLogin() {
 | 
					function doLogin() {
 | 
				
			||||||
    localStorage.setItem("email", $("#email").val());
 | 
					    localStorage.setItem("email", $("#email").val());
 | 
				
			||||||
    localStorage.setItem("password", $("#password").val());
 | 
					    localStorage.setItem("password", $("#password").val());
 | 
				
			||||||
 | 
					    $("#email, #password").val("");
 | 
				
			||||||
    loginFromLocalStorage();
 | 
					    loginFromLocalStorage();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,10 +21,12 @@ function loginFromLocalStorage() {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    req.done(data => {
 | 
					    req.done(data => {
 | 
				
			||||||
        $("#login-view").addClass("d-none");
 | 
					        if (single.getCurrentPath() == "/webui/") {
 | 
				
			||||||
        $("#main-view").removeClass("d-none");
 | 
					            single.loadRoute("/webui/inventory");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        $(".displayname").text(data.DisplayName);
 | 
					        $(".displayname").text(data.DisplayName);
 | 
				
			||||||
        window.accountId = data.id;
 | 
					        window.accountId = data.id;
 | 
				
			||||||
 | 
					        window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce;
 | 
				
			||||||
        updateInventory();
 | 
					        updateInventory();
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    req.fail(() => {
 | 
					    req.fail(() => {
 | 
				
			||||||
@ -35,14 +38,25 @@ function loginFromLocalStorage() {
 | 
				
			|||||||
function logout() {
 | 
					function logout() {
 | 
				
			||||||
    localStorage.removeItem("email");
 | 
					    localStorage.removeItem("email");
 | 
				
			||||||
    localStorage.removeItem("password");
 | 
					    localStorage.removeItem("password");
 | 
				
			||||||
    $("#login-view").removeClass("d-none");
 | 
					 | 
				
			||||||
    $("#main-view").addClass("d-none");
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (localStorage.getItem("email") && localStorage.getItem("password")) {
 | 
					if (localStorage.getItem("email") && localStorage.getItem("password")) {
 | 
				
			||||||
    loginFromLocalStorage();
 | 
					    loginFromLocalStorage();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					single.on("route_load", function (event) {
 | 
				
			||||||
 | 
					    if (event.route.paths[0] != "/webui/") {
 | 
				
			||||||
 | 
					        // Authorised route?
 | 
				
			||||||
 | 
					        if (!localStorage.getItem("email")) {
 | 
				
			||||||
 | 
					            // Not logged in?
 | 
				
			||||||
 | 
					            return single.loadRoute("/webui/"); // Show login screen
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        $("body").addClass("logged-in");
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        $("body").removeClass("logged-in");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.itemListPromise = new Promise(resolve => {
 | 
					window.itemListPromise = new Promise(resolve => {
 | 
				
			||||||
    const req = $.get("/custom/getItemLists");
 | 
					    const req = $.get("/custom/getItemLists");
 | 
				
			||||||
    req.done(data => {
 | 
					    req.done(data => {
 | 
				
			||||||
@ -61,7 +75,7 @@ window.itemListPromise = new Promise(resolve => {
 | 
				
			|||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateInventory() {
 | 
					function updateInventory() {
 | 
				
			||||||
    const req = $.get("/api/inventory.php?accountId=" + window.accountId);
 | 
					    const req = $.get("/api/inventory.php?" + window.authz);
 | 
				
			||||||
    req.done(data => {
 | 
					    req.done(data => {
 | 
				
			||||||
        window.itemListPromise.then(itemMap => {
 | 
					        window.itemListPromise.then(itemMap => {
 | 
				
			||||||
            document.getElementById("warframe-list").innerHTML = "";
 | 
					            document.getElementById("warframe-list").innerHTML = "";
 | 
				
			||||||
@ -215,7 +229,7 @@ function addGearExp(category, oid, xp) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    $.post({
 | 
					    $.post({
 | 
				
			||||||
        url: "/api/missionInventoryUpdate.php?accountId=" + window.accountId,
 | 
					        url: "/api/missionInventoryUpdate.php?" + window.authz,
 | 
				
			||||||
        contentType: "text/plain",
 | 
					        contentType: "text/plain",
 | 
				
			||||||
        data: JSON.stringify(data)
 | 
					        data: JSON.stringify(data)
 | 
				
			||||||
    }).done(function () {
 | 
					    }).done(function () {
 | 
				
			||||||
@ -235,7 +249,7 @@ function disposeOfGear(category, oid) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    $.post({
 | 
					    $.post({
 | 
				
			||||||
        url: "/api/sell.php?accountId=" + window.accountId,
 | 
					        url: "/api/sell.php?" + window.authz,
 | 
				
			||||||
        contentType: "text/plain",
 | 
					        contentType: "text/plain",
 | 
				
			||||||
        data: JSON.stringify(data)
 | 
					        data: JSON.stringify(data)
 | 
				
			||||||
    }).done(function () {
 | 
					    }).done(function () {
 | 
				
			||||||
@ -250,7 +264,7 @@ function doAcquireMiscItems() {
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    $.post({
 | 
					    $.post({
 | 
				
			||||||
        url: "/api/missionInventoryUpdate.php?accountId=" + window.accountId,
 | 
					        url: "/api/missionInventoryUpdate.php?" + window.authz,
 | 
				
			||||||
        contentType: "text/plain",
 | 
					        contentType: "text/plain",
 | 
				
			||||||
        data: JSON.stringify({
 | 
					        data: JSON.stringify({
 | 
				
			||||||
            MiscItems: [
 | 
					            MiscItems: [
 | 
				
			||||||
@ -268,3 +282,66 @@ function doAcquireMiscItems() {
 | 
				
			|||||||
$("#miscitem-name").on("input", () => {
 | 
					$("#miscitem-name").on("input", () => {
 | 
				
			||||||
    $("#miscitem-name").removeClass("is-invalid");
 | 
					    $("#miscitem-name").removeClass("is-invalid");
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function doAcquireRiven() {
 | 
				
			||||||
 | 
					    let fingerprint;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					        fingerprint = JSON.parse($("#addriven-fingerprint").val());
 | 
				
			||||||
 | 
					        if (typeof fingerprint !== "object") {
 | 
				
			||||||
 | 
					            fingerprint = JSON.parse(fingerprint);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } catch (e) {}
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					        typeof fingerprint !== "object" ||
 | 
				
			||||||
 | 
					        !("compat" in fingerprint) ||
 | 
				
			||||||
 | 
					        !("pol" in fingerprint) ||
 | 
				
			||||||
 | 
					        !("buffs" in fingerprint)
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        $("#addriven-fingerprint").addClass("is-invalid").focus();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const uniqueName = "/Lotus/Upgrades/Mods/Randomized/" + $("#addriven-type").val();
 | 
				
			||||||
 | 
					    // Add riven type to inventory
 | 
				
			||||||
 | 
					    $.post({
 | 
				
			||||||
 | 
					        url: "/api/missionInventoryUpdate.php?" + window.authz,
 | 
				
			||||||
 | 
					        contentType: "text/plain",
 | 
				
			||||||
 | 
					        data: JSON.stringify({
 | 
				
			||||||
 | 
					            RawUpgrades: [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    ItemType: uniqueName,
 | 
				
			||||||
 | 
					                    ItemCount: 1
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }).done(function () {
 | 
				
			||||||
 | 
					        // Get riven's assigned id
 | 
				
			||||||
 | 
					        $.get("/api/inventory.php?" + window.authz).done(data => {
 | 
				
			||||||
 | 
					            for (const rawUpgrade of data.RawUpgrades) {
 | 
				
			||||||
 | 
					                if (rawUpgrade.ItemType === uniqueName) {
 | 
				
			||||||
 | 
					                    // Add fingerprint to riven
 | 
				
			||||||
 | 
					                    $.post({
 | 
				
			||||||
 | 
					                        url: "/api/artifacts.php?" + window.authz,
 | 
				
			||||||
 | 
					                        contentType: "text/plain",
 | 
				
			||||||
 | 
					                        data: JSON.stringify({
 | 
				
			||||||
 | 
					                            Upgrade: {
 | 
				
			||||||
 | 
					                                ItemType: uniqueName,
 | 
				
			||||||
 | 
					                                UpgradeFingerprint: JSON.stringify(fingerprint),
 | 
				
			||||||
 | 
					                                ItemId: rawUpgrade.LastAdded
 | 
				
			||||||
 | 
					                            },
 | 
				
			||||||
 | 
					                            LevelDiff: 0,
 | 
				
			||||||
 | 
					                            Cost: 0,
 | 
				
			||||||
 | 
					                            FusionPointCost: 0
 | 
				
			||||||
 | 
					                        })
 | 
				
			||||||
 | 
					                    }).done(function () {
 | 
				
			||||||
 | 
					                        alert("Successfully added.");
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$("#addriven-fingerprint").on("input", () => {
 | 
				
			||||||
 | 
					    $("#addriven-fingerprint").removeClass("is-invalid");
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										317
									
								
								static/webui/single.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								static/webui/single.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,317 @@
 | 
				
			|||||||
 | 
					(function () {
 | 
				
			||||||
 | 
					    let head_include = document.body == null,
 | 
				
			||||||
 | 
					        style = document.createElement("style");
 | 
				
			||||||
 | 
					    style.textContent = `[data-route]:not(.route-visible){display:none}`;
 | 
				
			||||||
 | 
					    document.head.appendChild(style);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class EventEmitter {
 | 
				
			||||||
 | 
					        constructor() {
 | 
				
			||||||
 | 
					            this.event_handlers = {};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        on(event_name, func) {
 | 
				
			||||||
 | 
					            if (typeof func != "function") {
 | 
				
			||||||
 | 
					                throw "Event handler has to be a function.";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.event_handlers[event_name] = func;
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        off(event_name) {
 | 
				
			||||||
 | 
					            delete this.event_handlers[event_name];
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fire(event_name, args) {
 | 
				
			||||||
 | 
					            if (event_name in this.event_handlers) {
 | 
				
			||||||
 | 
					                this.event_handlers[event_name].call(this, args);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Route extends EventEmitter {
 | 
				
			||||||
 | 
					        constructor(overlay, elm, paths) {
 | 
				
			||||||
 | 
					            super();
 | 
				
			||||||
 | 
					            this.overlay = overlay;
 | 
				
			||||||
 | 
					            this.elm = elm;
 | 
				
			||||||
 | 
					            this.paths = paths;
 | 
				
			||||||
 | 
					            this.title = undefined;
 | 
				
			||||||
 | 
					            if (elm.hasAttribute("data-title")) {
 | 
				
			||||||
 | 
					                this.title = this.elm.getAttribute("data-title");
 | 
				
			||||||
 | 
					                this.elm.removeAttribute("data-title");
 | 
				
			||||||
 | 
					            } else if (document.querySelector("title") != null) {
 | 
				
			||||||
 | 
					                this.title = document.querySelector("title").textContent;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                this.title = this.paths[0];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        get element() {
 | 
				
			||||||
 | 
					            return this.elm;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isCurrent() {
 | 
				
			||||||
 | 
					            return this.elm.classList.contains("route-current");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isVisible() {
 | 
				
			||||||
 | 
					            return this.elm.classList.contains("route-visible");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class MultiRoute extends Route {
 | 
				
			||||||
 | 
					        constructor(overlay, elm, paths_data) {
 | 
				
			||||||
 | 
					            if (overlay) {
 | 
				
			||||||
 | 
					                paths_data = paths_data.substr(9);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let paths = [];
 | 
				
			||||||
 | 
					            paths_data.split(",").forEach(name => {
 | 
				
			||||||
 | 
					                paths.push(name.trim());
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            super(overlay, elm, paths);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getCanonicalPath() {
 | 
				
			||||||
 | 
					            if (this.paths[0].substr(0, 1) == "/") {
 | 
				
			||||||
 | 
					                return this.paths[0];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (this.paths.length > 1) {
 | 
				
			||||||
 | 
					                return this.paths[1];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return "/" + this.paths[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class StandardRoute extends MultiRoute {
 | 
				
			||||||
 | 
					        constructor(overlay, elm, paths) {
 | 
				
			||||||
 | 
					            super(overlay, elm, elm.getAttribute("data-route"));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class RegexRoute extends Route {
 | 
				
			||||||
 | 
					        constructor(overlay, elm) {
 | 
				
			||||||
 | 
					            let regexp = elm.getAttribute("data-route").substr(2);
 | 
				
			||||||
 | 
					            if (overlay) {
 | 
				
			||||||
 | 
					                regexp = regexp.substr(9);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            super(overlay, elm, [regexp]);
 | 
				
			||||||
 | 
					            this.regex = new RegExp(regexp);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getArgs(path) {
 | 
				
			||||||
 | 
					            if (path === undefined) {
 | 
				
			||||||
 | 
					                path = single.getCurrentPath();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let res = this.regex.exec(path);
 | 
				
			||||||
 | 
					            if (res && res.length > 0) {
 | 
				
			||||||
 | 
					                return res;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class SingleApp extends EventEmitter {
 | 
				
			||||||
 | 
					        constructor() {
 | 
				
			||||||
 | 
					            super();
 | 
				
			||||||
 | 
					            this.routes = [];
 | 
				
			||||||
 | 
					            this.routes_populated = false;
 | 
				
			||||||
 | 
					            if (!head_include) {
 | 
				
			||||||
 | 
					                this.populateRoutes();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            window.onpopstate = event => {
 | 
				
			||||||
 | 
					                event.preventDefault();
 | 
				
			||||||
 | 
					                single.loadRoute();
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            this.timeouts = [];
 | 
				
			||||||
 | 
					            this.intervals = [];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        populateRoutes() {
 | 
				
			||||||
 | 
					            if (this.routes_populated) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            document.body.querySelectorAll("[data-route]").forEach(elm => {
 | 
				
			||||||
 | 
					                let data = elm.getAttribute("data-route"),
 | 
				
			||||||
 | 
					                    overlay = false;
 | 
				
			||||||
 | 
					                if (data.substr(0, 9) == "overlay: ") {
 | 
				
			||||||
 | 
					                    data = data.substr(9);
 | 
				
			||||||
 | 
					                    overlay = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (data.substr(0, 2) == "~ ") {
 | 
				
			||||||
 | 
					                    this.routes.push(new RegexRoute(overlay, elm));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    this.routes.push(new StandardRoute(overlay, elm));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            if (this.routes.length == 0) {
 | 
				
			||||||
 | 
					                console.error("[single.js] You need to define at least one route");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.routes.forEach(route => {
 | 
				
			||||||
 | 
					                route.paths.forEach(path => {
 | 
				
			||||||
 | 
					                    for (let i = 0; i < this.routes; i++) {
 | 
				
			||||||
 | 
					                        if (this.routes[i] !== route && this.routes[i].paths.indexOf(path) > -1) {
 | 
				
			||||||
 | 
					                            console.error("[single.js] Duplicate path: " + path);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            document.body.addEventListener("click", event => {
 | 
				
			||||||
 | 
					                let elm = event.target;
 | 
				
			||||||
 | 
					                while (elm && !(elm instanceof HTMLAnchorElement)) {
 | 
				
			||||||
 | 
					                    elm = elm.parentNode;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (
 | 
				
			||||||
 | 
					                    elm instanceof HTMLAnchorElement &&
 | 
				
			||||||
 | 
					                    !elm.hasAttribute("target") &&
 | 
				
			||||||
 | 
					                    elm.hasAttribute("href") &&
 | 
				
			||||||
 | 
					                    elm.getAttribute("href").substr(0, 1) == "/"
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                    event.preventDefault();
 | 
				
			||||||
 | 
					                    single.loadRoute(new URL(elm.href));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            this.routes_populated = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getRoute(route) {
 | 
				
			||||||
 | 
					            this.populateRoutes();
 | 
				
			||||||
 | 
					            let is_elm = route instanceof HTMLElement;
 | 
				
			||||||
 | 
					            if (is_elm) {
 | 
				
			||||||
 | 
					                if (!route.hasAttribute("data-route")) {
 | 
				
			||||||
 | 
					                    throw "Invalid route element: " + route;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                route = route.getAttribute("data-route");
 | 
				
			||||||
 | 
					                if (route.substr(0, 9) == "overlay: ") {
 | 
				
			||||||
 | 
					                    route = route.substr(9);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (route.substr(0, 2) == "~ ") {
 | 
				
			||||||
 | 
					                    route = route.substr(2);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    route = route.split(",")[0];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (route.substr(0, 9) == "overlay: ") {
 | 
				
			||||||
 | 
					                    route = route.substr(9);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (route.substr(0, 2) == "~ ") {
 | 
				
			||||||
 | 
					                    route = route.substr(2);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (let i = 0; i < this.routes.length; i++) {
 | 
				
			||||||
 | 
					                if (this.routes[i].paths.indexOf(route) > -1) {
 | 
				
			||||||
 | 
					                    return this.routes[i];
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!is_elm) {
 | 
				
			||||||
 | 
					                return this.getRoutes(route)[0];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getRoutes(route) {
 | 
				
			||||||
 | 
					            let routes = [];
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                document.querySelectorAll(route).forEach(elm => {
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        let route = this.getRoute(elm);
 | 
				
			||||||
 | 
					                        if (route) {
 | 
				
			||||||
 | 
					                            routes.push(route);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } catch (ignored) {}
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            } catch (ignored) {}
 | 
				
			||||||
 | 
					            return routes;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loadRoute(path) {
 | 
				
			||||||
 | 
					            this.populateRoutes();
 | 
				
			||||||
 | 
					            this.timeouts.forEach(clearTimeout);
 | 
				
			||||||
 | 
					            this.intervals.forEach(clearInterval);
 | 
				
			||||||
 | 
					            if (path === undefined) {
 | 
				
			||||||
 | 
					                path = new URL(location.href);
 | 
				
			||||||
 | 
					            } else if (typeof path == "string" && path.substr(0, 1) == "/") {
 | 
				
			||||||
 | 
					                path = new URL(location.protocol + location.hostname + path);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let route,
 | 
				
			||||||
 | 
					                args = false,
 | 
				
			||||||
 | 
					                urlextra = "";
 | 
				
			||||||
 | 
					            if (path instanceof URL) {
 | 
				
			||||||
 | 
					                urlextra = path.search + path.hash;
 | 
				
			||||||
 | 
					                path = decodeURIComponent(path.pathname);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            for (let i = 0; i < this.routes.length; i++) {
 | 
				
			||||||
 | 
					                if (this.routes[i] instanceof RegexRoute) {
 | 
				
			||||||
 | 
					                    args = this.routes[i].getArgs(path);
 | 
				
			||||||
 | 
					                    if (args !== false) {
 | 
				
			||||||
 | 
					                        route = this.routes[i];
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (this.routes[i].paths.indexOf(path) > -1) {
 | 
				
			||||||
 | 
					                    route = this.routes[i];
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (route === undefined) {
 | 
				
			||||||
 | 
					                route = this.getRoute("404");
 | 
				
			||||||
 | 
					                if (route === null) {
 | 
				
			||||||
 | 
					                    route = this.routes[0];
 | 
				
			||||||
 | 
					                    path = route.getCanonicalPath();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (path.substr(0, 1) != "/") {
 | 
				
			||||||
 | 
					                path = route.getCanonicalPath();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (args === false) {
 | 
				
			||||||
 | 
					                args = [path];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            route.fire("beforeload", args);
 | 
				
			||||||
 | 
					            this.fire("route_beforeload", {
 | 
				
			||||||
 | 
					                route: route,
 | 
				
			||||||
 | 
					                args: args
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            this.routes.forEach(r => {
 | 
				
			||||||
 | 
					                if (r !== route) {
 | 
				
			||||||
 | 
					                    r.elm.classList.remove("route-current");
 | 
				
			||||||
 | 
					                    if (!route.overlay || r.overlay) {
 | 
				
			||||||
 | 
					                        r.elm.classList.remove("route-visible");
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            route.elm.classList.add("route-current", "route-visible");
 | 
				
			||||||
 | 
					            path += urlextra;
 | 
				
			||||||
 | 
					            if (this.getCurrentPath() != path) {
 | 
				
			||||||
 | 
					                history.pushState({}, route.title, path);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            document.querySelector("title").textContent = route.title;
 | 
				
			||||||
 | 
					            this.fire("route_load", {
 | 
				
			||||||
 | 
					                route: route,
 | 
				
			||||||
 | 
					                args: args
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            route.fire("load", args);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getCurrentRoute() {
 | 
				
			||||||
 | 
					            return this.getRoute(".route-current");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getCurrentPath() {
 | 
				
			||||||
 | 
					            return location.pathname + location.search + location.hash;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setTimeout(f, i) {
 | 
				
			||||||
 | 
					            this.timeouts.push(window.setTimeout(f, i));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setInterval(f, i) {
 | 
				
			||||||
 | 
					            this.intervals.push(window.setInterval(f, i));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.assert(!("single" in window));
 | 
				
			||||||
 | 
					    window.single = new SingleApp();
 | 
				
			||||||
 | 
					    if (["interactive", "complete"].indexOf(document.readyState) > -1) {
 | 
				
			||||||
 | 
					        window.single.loadRoute();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        document.addEventListener("DOMContentLoaded", () => window.single.loadRoute());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					})();
 | 
				
			||||||
							
								
								
									
										17
									
								
								static/webui/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								static/webui/style.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					@media (min-width: 992px) {
 | 
				
			||||||
 | 
					    body.logged-in #main-view {
 | 
				
			||||||
 | 
					        display: grid;
 | 
				
			||||||
 | 
					        grid-template-columns: 1fr 8fr;
 | 
				
			||||||
 | 
					        gap: 1.5rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    body:not(.logged-in) #sidebar {
 | 
				
			||||||
 | 
					        display: none;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					body:not(.logged-in) .navbar-toggler,
 | 
				
			||||||
 | 
					body:not(.logged-in) .nav-item.dropdown,
 | 
				
			||||||
 | 
					body:not(.logged-in) #refresh-note {
 | 
				
			||||||
 | 
					    display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user