dzura/www/js/scene.js

1061 lines
36 KiB
JavaScript

const SCENES = {
Init: class{
constructor() { }
load() {
LOAD(SCENES.Offline);
RECONNECT();
return true;
}
},
Offline:class{
constructor() { }
load() {
UI.nav([
UI.button(LANG("reconnect"), () => { RECONNECT(); }),
UI.button(LANG("practice"), () => { LOAD(SCENES.GamePractice); }),
], []);
return true;
}
message(code, data) {
switch(code) {
case OpCode.Hello: {
console.log("CLIENT VERSION: '" + CONFIG_VERSION + "'");
console.log("SERVER VERSION: '" + data.version + "'");
if(CONFIG_VERSION == data.version) {
RESUME();
} else {
location.reload();
}
} break;
}
}
},
Register:class{
constructor() { }
load() {
if(sessionStorage.getItem("auth") !== null) return false;
UI.mainmenu("register");
UI.mainnav([], [], { auth:true });
let container = document.createElement("section");
let form = document.createElement("form");
form.appendChild(UI.table(null, [
[ UI.label(LANG("handle"), "handle"), UI.textbox("handle", "") ],
[ UI.label(LANG("secret"), "secret"), UI.password("secret") ],
[ UI.label(LANG("invitation"), "code"), UI.password("code") ],
]));
let button = UI.submit(LANG("register"));
button.setAttribute("id", "submit");
form.appendChild(button);
form.addEventListener("submit", (event) => {
event.preventDefault();
let handle = document.getElementById("handle");
let secret = document.getElementById("secret");
let code = document.getElementById("code");
handle.removeAttribute("class");
secret.removeAttribute("class");
code.removeAttribute("class");
event.target.removeAttribute("class");
if(handle.value.length > 0 && secret.value.length > 0 && code.value.length > 0) {
event.target.setAttribute("disabled", "");
let enc = new TextEncoder();
let enc_handle = enc.encode(handle.value);
let enc_secret = enc.encode(secret.value);
let enc_code = enc.encode(code.value);
MESSAGE_COMPOSE([
PACK.u16(OpCode.Register),
PACK.u16(enc_handle.length),
enc_handle,
PACK.u16(enc_secret.length),
enc_secret,
PACK.u16(enc_code.length),
enc_code,
]);
} else {
if(handle.value.length == 0) { handle.setAttribute("class", "error"); }
if(secret.value.length == 0) { secret.setAttribute("class", "error"); }
if(code.value.length == 0) { code.setAttribute("class", "error"); }
}
});
container.appendChild(form);
MAIN.appendChild(container);
MAIN.setAttribute("class", "form");
document.getElementById("handle").focus();
return true;
}
message(code, data) {
if(code == OpCode.Register && data !== null) {
let submit = document.getElementById("submit");
switch(data.status) {
case Status.Ok: {
let b64_token = PACK.base64(data.token);
let b64_secret = PACK.base64(data.secret);
sessionStorage.setItem("auth", b64_token);
sessionStorage.setItem("auth_secret", b64_secret);
LOAD_URL();
} break;
default: {
submit.removeAttribute("disabled");
switch(data.status) {
case Status.BadHandle: {
document.getElementById("handle").setAttribute("class", "error");
submit.setAttribute("class", "error");
} break;
case Status.BadSecret: {
document.getElementById("secret").setAttribute("class", "error");
submit.setAttribute("class", "error");
} break;
case Status.BadCode: {
document.getElementById("code").setAttribute("class", "error");
submit.setAttribute("class", "error");
} break;
}
}
}
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
Authenticate:class{
constructor() { }
load() {
if(sessionStorage.getItem("auth") !== null) return false;
UI.mainmenu("authenticate");
UI.mainnav([], [], { auth:true });
let container = document.createElement("section");
let form = document.createElement("form");
form.appendChild(UI.table(null, [
[ UI.label(LANG("handle"), "handle"), UI.textbox("handle", "") ],
[ UI.label(LANG("secret"), "secret"), UI.password("secret") ],
]));
let button = UI.submit(LANG("login"));
button.setAttribute("id", "submit");
form.appendChild(button);
form.addEventListener("submit", (event) => {
event.preventDefault();
let handle = document.getElementById("handle");
let secret = document.getElementById("secret");
handle.removeAttribute("class");
secret.removeAttribute("class");
event.target.removeAttribute("class");
if(handle.value.length > 0 && secret.value.length > 0) {
event.target.setAttribute("disabled", "");
let enc = new TextEncoder();
let enc_handle = enc.encode(handle.value);
let enc_secret = enc.encode(secret.value);
MESSAGE_COMPOSE([
PACK.u16(OpCode.Authenticate),
PACK.u16(enc_handle.length),
enc_handle,
PACK.u16(enc_secret.length),
enc_secret,
]);
} else {
if(handle.value.length == 0) { handle.setAttribute("class", "error"); }
if(secret.value.length == 0) { secret.setAttribute("class", "error"); }
}
});
container.appendChild(form);
MAIN.appendChild(container);
MAIN.setAttribute("class", "form");
document.getElementById("handle").focus();
return true;
}
message(code, data) {
if(code == OpCode.Authenticate && data !== null) {
let submit = document.getElementById("submit");
switch(data.status) {
case Status.Ok: {
sessionStorage.setItem("auth", PACK.base64(data.token));
sessionStorage.setItem("auth_secret", PACK.base64(data.secret));
LOAD_URL();
} break;
case Status.Error: {
submit.removeAttribute("disabled");
document.getElementById("handle").setAttribute("class", "error");
document.getElementById("secret").setAttribute("class", "error");
submit.setAttribute("class", "error");
}
}
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
Browse:class{
constructor() {
this.page = 1;
this.pages = 1;
}
load() {
if(SOCKET !== null) {
UI.mainmenu("browse");
let indicator_page = UI.div([]);
indicator_page.setAttribute("id", "indicator-page");
UI.mainnav(
[ ],
[
indicator_page,
UI.button("◀", () => {
if(SCENE.page < SCENE.pages) { SCENE.page += 1; }
SCENE.refresh();
}),
UI.button("▶", () => {
if(SCENE.page > 1) { SCENE.page -= 1; }
SCENE.refresh();
}),
UI.button(LANG("refresh"), () => { SCENE.referesh(); }),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list session");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura", "/");
} else {
LOAD(SCENES.Offline);
}
return true;
}
refresh() {
MESSAGE_SESSION_LIST(this.page, 2, false, false);
}
message(code, data) {
switch(code) {
case OpCode.SessionList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
UI.page_indicator((data.records.length > 0)? 1 : 0, data.records.length, data.records.length);
table.appendChild(UI.session_table(data.records));
}
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
Continue:class{
constructor() {
this.page = 1;
this.pages = 1;
}
load() {
if(sessionStorage.getItem("auth") === null) return false;
UI.mainmenu("continue");
let indicator_page = UI.div([]);
indicator_page.setAttribute("id", "indicator-page");
UI.mainnav(
[ ],
[
indicator_page,
UI.button("◀", () => {
if(SCENE.page < SCENE.pages) { SCENE.page += 1; }
SCENE.refresh();
}),
UI.button("▶", () => {
if(SCENE.page > 1) { SCENE.page -= 1; }
SCENE.refresh();
}),
UI.button(LANG("refresh"), () => { SCENE.referesh(); }),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list session-resume");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura - Continue", "/continue/");
return true;
}
refresh() {
MESSAGE_SESSION_LIST(this.page, 2, true, false);
}
message(code, data) {
switch(code) {
case OpCode.SessionList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
UI.page_indicator((data.records.length > 0)? 1 : 0, data.records.length, data.records.length);
table.appendChild(UI.session_table_resume(data.records));
}
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
/*Join:{
load() {
if(sessionStorage.getItem("auth") === null) return false;
CONTEXT.Data = {
page:0,
records:[],
};
UI.mainmenu("join");
UI.mainnav(
[ ],
[
UI.div([UI.text("0 - 0 of 0")]),
UI.button("◀", null),
UI.button("▶", null),
UI.button("Refresh", null),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura - Join", "/join/");
return true;
},
refresh() {
MESSAGE_SESSION_LIST(0, 1, false, false);
},
message(code, data) {
switch(code) {
case OpCode.SessionList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
table.appendChild(UI.session_table_join(data.records));
}
} break;
case OpCode.SessionView: {
if(data.status == Status.Ok) {
LOAD(SCENES.Game, data);
}
} break;
}
},
disconnect() {
LOAD(SCENES.Offline);
},
},*/
Live:class{
constructor() {
this.page = 1;
this.pages = 1;
}
load() {
UI.mainmenu("live");
let indicator_page = UI.div([]);
indicator_page.setAttribute("id", "indicator-page");
UI.mainnav(
[ ],
[
indicator_page,
UI.button("◀", () => {
if(SCENE.page < SCENE.pages) { SCENE.page += 1; }
SCENE.refresh();
}),
UI.button("▶", () => {
if(SCENE.page > 1) { SCENE.page -= 1; }
SCENE.refresh();
}),
UI.button(LANG("refresh"), () => { SCENE.referesh(); }),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list session");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura - Live", "/live/");
return true;
}
refresh() {
MESSAGE_SESSION_LIST(this.page, 2, false, true);
}
message(code, data) {
switch(code) {
case OpCode.SessionList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
UI.page_indicator((data.records.length > 0)? 1 : 0, data.records.length, data.records.length);
table.appendChild(UI.session_table(data.records));
}
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
History:class{
constructor() {
this.page = 1;
this.pages = 1;
}
load() {
UI.mainmenu("history");
let indicator_page = UI.div([]);
indicator_page.setAttribute("id", "indicator-page");
UI.mainnav(
[ ],
[
indicator_page,
UI.button("◀", () => {
if(SCENE.page < SCENE.pages) { SCENE.page += 1; }
SCENE.refresh();
}),
UI.button("▶", () => {
if(SCENE.page > 1) { SCENE.page -= 1; }
SCENE.refresh();
}),
UI.button(LANG("refresh"), () => { SCENE.referesh(); }),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list session");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura - History", "/history/");
return true;
}
refresh() {
MESSAGE_SESSION_LIST(this.page, 3, false, false);
}
message(code, data) {
switch(code) {
case OpCode.SessionList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
UI.page_indicator((data.records.length > 0)? 1 : 0, data.records.length, data.records.length);
table.appendChild(UI.session_table_history(data.records));
}
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
Guide:class{
constructor() { }
load() {
UI.mainmenu("guide");
UI.mainnav([
UI.button("Game", () => { SCENE.refresh("game.html"); }),
UI.button("Pieces", () => { SCENE.refresh("pieces.html"); }),
UI.button("Interface", () => { SCENE.refresh("interface.html"); }),
], []);
let body = document.createElement("article");
body.setAttribute("id", "article");
MAIN.appendChild(body);
this.refresh("game.html");
history.pushState(null, "Dzura - Guide", "/guide/");
return true;
}
refresh(page) {
fetch("/guide/" + page)
.then((response) => {
return response.text();
})
.then((text) => {
let parser = new DOMParser();
let body = parser.parseFromString(text, "text/html");
document.getElementById("article").innerHTML = body.body.innerHTML;
});
}
},
About:class{
constructor() { }
load() {
UI.mainmenu("about");
UI.mainnav([], []);
let body = document.createElement("article");
body.setAttribute("id", "article");
MAIN.appendChild(body);
this.refresh("main.html");
history.pushState(null, "Dzura - About", "/about/");
return true;
}
refresh(page) {
fetch("/about/" + page)
.then((response) => {
return response.text();
})
.then((text) => {
let parser = new DOMParser();
let body = parser.parseFromString(text, "text/html");
document.getElementById("article").innerHTML = body.body.innerHTML;
});
}
},
Extras:class{
constructor() { }
load() {
UI.mainmenu("extras");
UI.mainnav([], []);
history.pushState(null, "Dzura - About", "/extras/");
return true;
}
},
GameLoad:class{
constructor() {
this.mode = null;
this.token = null;
this.turn = null;
}
load(data) {
this.mode = data.mode;
this.token = data.token;
if(data.turn !== undefined) { this.turn = data.turn; }
MESSAGE_SESSION_VIEW(this.token, this.mode == INTERFACE.Mode.Player);
return true;
}
message(code, data) {
switch(code) {
case OpCode.SessionView: {
if(data.status == Status.Ok) {
LOAD(SCENES.Game, {
mode:this.mode,
token:this.token,
view:data,
turn:this.turn,
});
} else {
LOAD(SCENES.Browse);
}
} break;
}
}
},
Game:class{
constructor() {
this.game = null;
}
load(data) {
// Bottom Buttons
let buttons_bottom = [ ];
if(data.mode == INTERFACE.Mode.Player) {
let button_resign = UI.button(LANG("resign"), () => { INTERFACE.resign(); });
button_resign.setAttribute("id", "button-resign");
buttons_bottom.push(button_resign);
} else {
let button_react = UI.button("Clap", () => { INTERFACE.react(); });
buttons_bottom.push(button_react);
}
buttons_bottom.push(UI.button(LANG("back"), () => {
LOAD(SCENES.Browse);
}));
// Standard Top Buttons
let buttons_top = [
UI.button(LANG("rotate"), () => { INTERFACE.rotate(); }),
UI.button(LANG("mirror"), () => { INTERFACE.mirror(); }),
];
// Resume / Review Buttons
if(data.view.player != 0) {
if(data.mode == INTERFACE.Mode.Review) {
if(!data.view.is_complete) {
let callback_resume = function() {
LOAD(SCENES.GameLoad, {
token:this.token,
mode:INTERFACE.Mode.Player,
});
}
callback_resume = callback_resume.bind({
token: data.token,
player: data.player,
});
buttons_top.push(UI.button(LANG("resume"), callback_resume));
}
} else {
let callback_review = function() {
LOAD(SCENES.GameLoad, {
token:this.token,
mode:INTERFACE.Mode.Review,
});
}
callback_review = callback_review.bind({
token: data.token,
player: data.player,
});
buttons_top.push(UI.button(LANG("review"), callback_review));
}
}
// Practice Button
if(data.mode == INTERFACE.Mode.Review) {
let play_callback = function() {
LOAD(SCENES.GamePractice, {
token:this.token,
player:this.player,
history:INTERFACE_DATA.history,
turn:INTERFACE_DATA.replay_turn,
})
}
play_callback = play_callback.bind({
token: data.token,
player: data.player,
});
buttons_top.push(UI.button(LANG("practice"), play_callback));
}
UI.nav(buttons_top, buttons_bottom);
// Turn Indicators, Scroll Bar
if(data.mode == INTERFACE.Mode.Review) {
let ind_turn = UI.div([UI.text("0 / 0")]);
ind_turn.setAttribute("id", "indicator-turn");
UI.mainnav(
[ ],
[
UI.button(LANG("auto"), () => { INTERFACE.replay_toggle_auto(); }),
UI.button("◀", () => { INTERFACE.replay_off(); INTERFACE.replay_first(); }),
UI.button("◁", () => { INTERFACE.replay_off(); INTERFACE.replay_prev(); }),
ind_turn,
UI.button("▷", () => { INTERFACE.replay_off(); INTERFACE.replay_next(true); }),
UI.button("▶", () => { INTERFACE.replay_off(); INTERFACE.replay_last(); }),
]
);
let slider = UI.slider("turn-slider", (event) => { INTERFACE.replay_off(); INTERFACE.replay_jump(event.target.value, true); });
slider.setAttribute("min", "0");
slider.setAttribute("max", "0");
MAIN.appendChild(UI.div([ slider ], "turn-slider-padding"));
}
// Canvas
let canvas = document.createElement("canvas");
canvas.setAttribute("id", "game");
MAIN.appendChild(canvas);
// Interface
INTERFACE.init(data.token, data.mode);
if(data.turn !== null) {
INTERFACE_DATA.replay_turn = data.turn;
}
history.pushState(null, "Dzura - Game", "/game/" + PACK.base64(data.token).slice(0, -1));
return true;
}
unload() {
INTERFACE.uninit();
}
message(code, data) {
INTERFACE.message(code, data);
}
disconnect() {
LOAD(SCENES.Offline);
}
},
GamePractice:class{
constructor() {
this.game = null;
}
load(data) {
let buttons_top = [
UI.button(LANG("rotate"), () => { INTERFACE.rotate(); }),
UI.button(LANG("mirror"), () => { INTERFACE.mirror(); }),
UI.button(LANG("auto"), () => { INTERFACE.auto(); }),
];
if(data !== null) {
if(data.history.length > 0) {
let callback_review = function() {
LOAD(SCENES.GameLoad, {
token:this.token,
mode:INTERFACE.Mode.Review,
turn:INTERFACE_DATA.history_begin.length,
});
}
callback_review = callback_review.bind({
token: data.token,
player: data.player,
});
buttons_top.push(UI.button(LANG("review"), callback_review));
}
}
let buttons_bottom = [ ];
buttons_bottom.push(UI.button(LANG("undo"), () => { INTERFACE.undo(); }));
buttons_bottom.push(UI.button(LANG("reset"), () => { INTERFACE.reset(); }));
buttons_bottom.push(UI.button(LANG("back"), () => { LOAD(SCENES.Browse); }));
UI.nav(buttons_top, buttons_bottom);
let canvas = document.createElement("canvas");
canvas.setAttribute("id", "game");
MAIN.appendChild(canvas);
INTERFACE.init(data, INTERFACE.Mode.Local);
if(data !== null) {
for(let i = 0; i < data.turn; ++i) {
INTERFACE_DATA.history_begin.push(data.history[i]);
}
INTERFACE.reset();
}
history.pushState(null, "Dzura - Practice", "/practice/");
return true;
}
unload() {
INTERFACE.uninit();
}
},
/*Await:{
load() {
if(sessionStorage.getItem("auth") === null) return false;
UI.mainmenu("await");
UI.mainnav([], [], { session:true });
let container = document.createElement("section");
let form = document.createElement("div");
form.appendChild(UI.text("Await a challenger:"));
form.appendChild(UI.table(null, [
[ UI.label("Accepting", "in-accept"), UI.checkbox("in-accept") ],
]));
let button = UI.button("Update", (event) => {
let accepting = 0;
if(document.getElementById("in-accept").value == "on") {
accepting = 1;
}
let flags = 0;
flags |= accepting;
MESSAGE_COMPOSE([
PACK.u16(OpCode.UserAwait),
PACK.u32(flags),
]);
LOAD_URL();
});
form.appendChild(button);
container.appendChild(form);
MAIN.appendChild(container);
MAIN.setAttribute("class", "form");
return true;
},
disconnect() {
LOAD(SCENES.Offline);
},
},*/
Challenge:class{
constructor() {
this.page = 0;
}
load() {
if(sessionStorage.getItem("auth") === null) return false;
CONTEXT.Data = {
page:0,
records:[],
};
UI.mainmenu("browse");
UI.mainnav(
[ ],
[
UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]),
UI.button("◀", null),
UI.button("▶", null),
UI.button(LANG("refresh"), null),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list challenge");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura", "/challenge/");
return true;
}
refresh() {
MESSAGE_COMPOSE([
PACK.u16(OpCode.UserList),
]);
}
message(code, data) {
switch(code) {
case OpCode.UserList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
let rows = [ ];
for(let r = 0; r < data.users.length; ++r) {
let buttons = [ ];
let callback = function(event) {
MESSAGE_CHALLENGE(this.handle);
event.target.innerText = "Sent";
event.target.setAttribute("class", "highlight");
event.target.setAttribute("disabled", "");
};
callback = callback.bind({handle: data.users[r].handle});
buttons.push(UI.button(LANG("challenge"), callback));
let handle = UI.text(data.users[r].handle);
if(!data.users[r].is_online) {
handle = UI.span([handle], "text-system");
}
rows.push([
handle,
UI.text(LANG("unranked")),
buttons,
]);
}
let tbody = UI.table_content(
[ LANG("handle"), LANG("rank"), "" ],
rows,
);
table.appendChild(tbody);
}
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
ChallengeList:class{
constructor() { }
load() {
if(sessionStorage.getItem("auth") === null) return false;
CONTEXT.Data = {
page:0,
records:[],
};
UI.mainmenu("browse");
UI.mainnav(
[ ],
[
UI.div([UI.text(LANG_PAGEOF(0, 0, 0))]),
UI.button("◀", null),
UI.button("▶", null),
UI.button(LANG("refresh"), null),
],
{ auth:true, session:true }
);
let table = document.createElement("table");
table.setAttribute("id", "content");
table.setAttribute("class", "list challenge");
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Dzura", "/challenge/");
return true;
}
refresh() {
MESSAGE_COMPOSE([
PACK.u16(OpCode.ChallengeList),
]);
}
message(code, data) {
switch(code) {
case OpCode.ChallengeList: {
let table = document.getElementById("content");
UI.clear(table);
if(data !== null) {
let rows = [ ];
for(let r = 0; r < data.challenges.length; ++r) {
let buttons = [ ];
let callback_accept = function() {
MESSAGE_CHALLENGE_ANSWER(this.handle, true);
};
callback_accept = callback_accept.bind({handle: data.challenges[r].handle});
buttons.push(UI.button(LANG("accept"), callback_accept));
let callback_decline = function() {
MESSAGE_CHALLENGE_ANSWER(this.handle, false);
};
callback_decline = callback_decline.bind({handle: data.challenges[r].handle});
buttons.push(UI.button(LANG("decline"), callback_decline));
rows.push([
UI.text(data.challenges[r].handle),
UI.text(LANG("unranked")),
buttons,
]);
}
let tbody = UI.table_content(
[ LANG("handle"), LANG("rank"), "" ],
rows,
);
table.appendChild(tbody);
}
} break;
case OpCode.ChallengeAnswer: {
if(data.status == Status.Ok) {
LOAD(SCENES.GameLoad, {
token:this.token,
mode:INTERFACE.Mode.Player,
});
} else {
SCENE.refresh();
}
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);
}
},
};
function LOAD(scene, data=null) {
UNLOAD();
UI.rebuild();
SCENE = new scene();
if(!SCENE.load(data)) { LOAD(SCENES.Browse); }
UI.update_status();
}
function UNLOAD() {
if(SCENE !== null && SCENE.unload !== undefined) { SCENE.unload(); }
}
function LOAD_URL() {
let parts = window.location.pathname.split("/");
if(parts.length > 1) {
switch(parts[1]) {
case "continue": LOAD(SCENES.Continue); return;
case "live": LOAD(SCENES.Live); return;
case "history": LOAD(SCENES.History); return;
case "practice": LOAD(SCENES.GamePractice); return;
case "guide": LOAD(SCENES.Guide); return;
case "about": LOAD(SCENES.About); return;
case "challenge": LOAD(SCENES.Challenge); return;
case "game": {
if(parts[2]) {
let token = UNPACK.base64(parts[2] + "=");
LOAD(SCENES.GameLoad, {
token:token,
mode:INTERFACE.Mode.Review,
});
return;
}
} break;
}
}
LOAD(SCENES.Browse);
}