-
-
+
@@ -79,6 +151,12 @@
crossorigin="anonymous"
>
+
+
diff --git a/static/webui/script.js b/static/webui/script.js
index ef61d4f8..cf9d05fb 100644
--- a/static/webui/script.js
+++ b/static/webui/script.js
@@ -1,6 +1,7 @@
function doLogin() {
localStorage.setItem("email", $("#email").val());
localStorage.setItem("password", $("#password").val());
+ $("#email, #password").val("");
loginFromLocalStorage();
}
@@ -20,10 +21,12 @@ function loginFromLocalStorage() {
})
});
req.done(data => {
- $("#login-view").addClass("d-none");
- $("#main-view").removeClass("d-none");
+ if (single.getCurrentPath() == "/webui/") {
+ single.loadRoute("/webui/inventory");
+ }
$(".displayname").text(data.DisplayName);
window.accountId = data.id;
+ window.authz = "accountId=" + data.id + "&nonce=" + data.Nonce;
updateInventory();
});
req.fail(() => {
@@ -35,14 +38,25 @@ function loginFromLocalStorage() {
function logout() {
localStorage.removeItem("email");
localStorage.removeItem("password");
- $("#login-view").removeClass("d-none");
- $("#main-view").addClass("d-none");
}
if (localStorage.getItem("email") && localStorage.getItem("password")) {
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 => {
const req = $.get("/custom/getItemLists");
req.done(data => {
@@ -61,7 +75,7 @@ window.itemListPromise = new Promise(resolve => {
});
function updateInventory() {
- const req = $.get("/api/inventory.php?accountId=" + window.accountId);
+ const req = $.get("/api/inventory.php?" + window.authz);
req.done(data => {
window.itemListPromise.then(itemMap => {
document.getElementById("warframe-list").innerHTML = "";
@@ -215,7 +229,7 @@ function addGearExp(category, oid, xp) {
}
];
$.post({
- url: "/api/missionInventoryUpdate.php?accountId=" + window.accountId,
+ url: "/api/missionInventoryUpdate.php?" + window.authz,
contentType: "text/plain",
data: JSON.stringify(data)
}).done(function () {
@@ -235,7 +249,7 @@ function disposeOfGear(category, oid) {
}
];
$.post({
- url: "/api/sell.php?accountId=" + window.accountId,
+ url: "/api/sell.php?" + window.authz,
contentType: "text/plain",
data: JSON.stringify(data)
}).done(function () {
@@ -250,7 +264,7 @@ function doAcquireMiscItems() {
return;
}
$.post({
- url: "/api/missionInventoryUpdate.php?accountId=" + window.accountId,
+ url: "/api/missionInventoryUpdate.php?" + window.authz,
contentType: "text/plain",
data: JSON.stringify({
MiscItems: [
@@ -268,3 +282,66 @@ function doAcquireMiscItems() {
$("#miscitem-name").on("input", () => {
$("#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");
+});
diff --git a/static/webui/single.js b/static/webui/single.js
new file mode 100644
index 00000000..928d58e3
--- /dev/null
+++ b/static/webui/single.js
@@ -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());
+ }
+})();
diff --git a/static/webui/style.css b/static/webui/style.css
new file mode 100644
index 00000000..ac9bf2a1
--- /dev/null
+++ b/static/webui/style.css
@@ -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;
+}