summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2021-05-01 00:48:35 +0200
committerTor Andersson <tor@ccxvii.net>2021-05-01 00:48:35 +0200
commit7999bb461202d8914b6af1ea8f0c51d4a2886be3 (patch)
tree869e689f5cc70455c1168174cf2aa5104ff06378
parent652852e3104ce4020de53231ee7691a4970439d6 (diff)
downloadserver-7999bb461202d8914b6af1ea8f0c51d4a2886be3.tar.gz
Add common client code and grid stylesheet.
-rw-r--r--public/common/client.js375
-rw-r--r--public/common/grid.css302
-rw-r--r--public/images/chat-bubble.svg1
-rw-r--r--public/images/cog.svg1
-rw-r--r--public/images/earth-africa-europe.svg1
-rw-r--r--public/images/earth-america.svg1
-rw-r--r--public/images/earth-asia-oceania.svg1
-rw-r--r--public/images/scroll-quill.svg1
8 files changed, 683 insertions, 0 deletions
diff --git a/public/common/client.js b/public/common/client.js
new file mode 100644
index 0000000..ea1c12b
--- /dev/null
+++ b/public/common/client.js
@@ -0,0 +1,375 @@
+let socket = null;
+let chat_is_visible = false;
+let chat_text = null;
+let chat_key = null;
+let chat_last_day = null;
+let chat_log = null;
+
+function scroll_with_middle_mouse(panel_sel, multiplier) {
+ let panel = document.querySelector(panel_sel);
+ let down_x, down_y, scroll_x, scroll_y;
+ if (!multiplier)
+ multiplier = 1;
+ function md(e) {
+ if (e.button === 1) {
+ down_x = e.clientX;
+ down_y = e.clientY;
+ scroll_x = panel.scrollLeft;
+ scroll_y = panel.scrollTop;
+ window.addEventListener('mousemove', mm);
+ window.addEventListener('mouseup', mu);
+ e.preventDefault();
+ }
+ }
+ function mm(e) {
+ let dx = down_x - e.clientX;
+ let dy = down_y - e.clientY;
+ panel.scrollLeft = scroll_x + dx * multiplier;
+ panel.scrollTop = scroll_y + dy * multiplier;
+ e.preventDefault();
+ }
+ function mu(e) {
+ if (e.button === 1) {
+ window.removeEventListener('mousemove', mm);
+ window.removeEventListener('mouseup', mu);
+ e.preventDefault();
+ }
+ }
+ panel.addEventListener('mousedown', md);
+}
+
+function drag_element_with_mouse(element_sel, grabber_sel) {
+ let element = document.querySelector(element_sel);
+ let grabber = document.querySelector(grabber_sel) || element;
+ let save_x, save_y;
+ function md(e) {
+ if (e.button === 0) {
+ save_x = e.clientX;
+ save_y = e.clientY;
+ window.addEventListener('mousemove', mm);
+ window.addEventListener('mouseup', mu);
+ e.preventDefault();
+ }
+ }
+ function mm(e) {
+ let dx = save_x - e.clientX;
+ let dy = save_y - e.clientY;
+ save_x = e.clientX;
+ save_y = e.clientY;
+ element.style.left = (element.offsetLeft - dx) + "px";
+ element.style.top = (element.offsetTop - dy) + "px";
+ e.preventDefault();
+ }
+ function mu(e) {
+ if (e.button === 0) {
+ window.removeEventListener('mousemove', mm);
+ window.removeEventListener('mouseup', mu);
+ e.preventDefault();
+ }
+ }
+ grabber.addEventListener('mousedown', md);
+}
+
+function add_chat_lines(log) {
+ function format_time(date) {
+ let mm = date.getMinutes();
+ let hh = date.getHours();
+ if (mm < 10) mm = "0" + mm;
+ if (hh < 10) hh = "0" + hh;
+ return hh + ":" + mm;
+ }
+ function add_date_line(date) {
+ let line = document.createElement("div");
+ line.className = "date";
+ line.textContent = "~ " + date + " ~";
+ chat_text.appendChild(line);
+ }
+ function add_chat_line(time, user, message) {
+ let line = document.createElement("div");
+ line.textContent = "[" + time + "] " + user + " \xbb " + message;
+ chat_text.appendChild(line);
+ chat_text.scrollTop = chat_text.scrollHeight;
+ }
+ for (let entry of log) {
+ chat_log.push(entry);
+ let [date, user, message] = entry;
+ date = new Date(date);
+ let day = date.toDateString();
+ if (day != chat_last_day) {
+ add_date_line(day);
+ chat_last_day = day;
+ }
+ add_chat_line(format_time(date), user, message);
+ }
+}
+
+function load_chat(game) {
+ chat_key = "chat/" + game;
+ chat_text = document.querySelector(".chat_text");
+ chat_last_day = null;
+ chat_log = [];
+ let save = JSON.parse(window.localStorage.getItem(chat_key));
+ if (save) {
+ if (Date.now() < save.expires)
+ add_chat_lines(save.chat);
+ else
+ window.localStorage.removeItem(chat_key);
+ }
+ return chat_log.length;
+}
+
+function save_chat() {
+ const DAY = 86400000;
+ let save = { expires: Date.now() + 7 * DAY, chat: chat_log };
+ window.localStorage.setItem(chat_key, JSON.stringify(save));
+}
+
+function update_chat(log_start, log) {
+ if (log_start == 0) {
+ chat_last_day = null;
+ chat_log = [];
+ while (chat_text.firstChild)
+ chat_text.removeChild(chat_text.firstChild);
+ }
+ add_chat_lines(log);
+}
+
+function init_client(roles) {
+ let params = new URLSearchParams(window.location.search);
+ let title = window.location.pathname.split("/")[1];
+ let game = params.get("game");
+ let role = params.get("role");
+ let player = null;
+
+ const ROLE_SEL = [
+ ".role.one",
+ ".role.two",
+ ".role.three",
+ ".role.four",
+ ".role.five",
+ ".role.six",
+ ".role.seven",
+ ];
+
+ const USER_SEL = [
+ ".role.one .role_user",
+ ".role.two .role_user",
+ ".role.three .role_user",
+ ".role.four .role_user",
+ ".role.five .role_user",
+ ".role.six .role_user",
+ ".role.seven .role_user",
+ ];
+
+ load_chat(game);
+
+ console.log("JOINING game", game, "role", role);
+
+ socket = io({
+ transports: ['websocket'],
+ query: { title: title, game: game, role: role },
+ });
+
+ socket.on('connect', () => {
+ console.log("CONNECTED");
+ document.querySelector(".grid_top").classList.remove('disconnected');
+ socket.emit('getchat', chat_log.length); // only send new messages when we reconnect!
+ });
+
+ socket.on('disconnect', () => {
+ console.log("DISCONNECTED");
+ document.getElementById("prompt").textContent = "Disconnected from server!";
+ document.querySelector(".grid_top").classList.add('disconnected');
+ });
+
+ socket.on('roles', (me, players) => {
+ console.log("ROLES", me, JSON.stringify(players));
+ player = me;
+ if (player == "Observer")
+ document.querySelector(".chat_button").style.display = "none";
+ document.querySelector(".grid_top").classList.add(player);
+ for (let i = 0; i < roles.length; ++i) {
+ let p = players.find(p => p.role == roles[i]);
+ document.querySelector(USER_SEL[i]).textContent = p ? p.name : "NONE";
+ }
+ });
+
+ socket.on('presence', (presence) => {
+ console.log("PRESENCE", presence);
+ for (let i = 0; i < roles.length; ++i) {
+ let elt = document.querySelector(ROLE_SEL[i]);
+ if (roles[i] in presence)
+ elt.classList.add('present');
+ else
+ elt.classList.remove('present');
+ }
+ });
+
+ socket.on('state', (state) => {
+ console.log("STATE");
+ on_update_log(state);
+ on_update_bar(state, player);
+ on_update(state, player);
+ });
+
+ socket.on('save', (msg) => {
+ console.log("SAVE");
+ window.localStorage[title + '/save'] = msg;
+ });
+
+ socket.on('error', (msg) => {
+ console.log("ERROR", msg);
+ document.getElementById("prompt").textContent = msg;
+ });
+
+ socket.on('chat', function (log_start, log) {
+ console.log("CHAT UPDATE", log_start, log.length);
+ update_chat(log_start, log);
+ let button = document.querySelector(".chat_button");
+ if (!chat_is_visible)
+ button.classList.add("new");
+ else
+ save_chat();
+ });
+
+ document.querySelector(".chat_form").addEventListener("submit", e => {
+ let input = document.querySelector("#chat_input");
+ e.preventDefault();
+ if (input.value) {
+ socket.emit('chat', input.value);
+ input.value = '';
+ } else {
+ hide_chat();
+ }
+ });
+
+ document.querySelector("body").addEventListener("keydown", e => {
+ if (player && player != "Observer") {
+ if (e.key == "Escape") {
+ if (chat_is_visible) {
+ e.preventDefault();
+ hide_chat();
+ }
+ }
+ if (e.key == "Enter") {
+ let input = document.querySelector("#chat_input");
+ if (document.activeElement != input) {
+ e.preventDefault();
+ show_chat();
+ }
+ }
+ }
+ });
+
+ drag_element_with_mouse(".chat_window", ".chat_header");
+}
+
+function on_update_bar(state, player) {
+ document.getElementById("prompt").textContent = state.prompt;
+ if (state.actions)
+ document.querySelector(".grid_top").classList.add("your_turn");
+ else
+ document.querySelector(".grid_top").classList.remove("your_turn");
+}
+
+function on_update_log(state) {
+ let parent = document.getElementById("log");
+ let to_delete = parent.children.length - state.log_start;
+ while (to_delete > 0) {
+ parent.removeChild(parent.firstChild);
+ --to_delete;
+ }
+ for (let entry of state.log) {
+ let p = document.createElement("div");
+ p.textContent = entry;
+ parent.prepend(p);
+ }
+}
+
+function toggle_fullscreen() {
+ if (document.fullscreen)
+ document.exitFullscreen();
+ else
+ document.documentElement.requestFullscreen();
+}
+
+function show_chat() {
+ if (!chat_is_visible) {
+ document.querySelector(".chat_button").classList.remove("new");
+ document.querySelector(".chat_window").classList.add("show");
+ document.querySelector("#chat_input").focus();
+ chat_is_visible = true;
+ save_chat();
+ }
+}
+
+function hide_chat() {
+ if (chat_is_visible) {
+ document.querySelector(".chat_window").classList.remove("show");
+ document.querySelector("#chat_input").blur();
+ chat_is_visible = false;
+ }
+}
+
+function toggle_chat() {
+ if (chat_is_visible)
+ hide_chat();
+ else
+ show_chat();
+}
+
+function toggle_log() {
+ document.querySelector(".grid_window").classList.toggle("hide_log");
+}
+
+function show_action_button(sel, action, use_label = false) {
+ let button = document.querySelector(sel);
+ if (game.actions && action in game.actions) {
+ button.classList.remove("hide");
+ if (game.actions[action]) {
+ if (use_label)
+ button.textContent = game.actions[action];
+ button.disabled = false;
+ } else {
+ button.disabled = true;
+ }
+ } else {
+ button.classList.add("hide");
+ }
+}
+
+function confirm_resign() {
+ if (window.confirm("Are you sure that you want to resign?"))
+ socket.emit('resign');
+}
+
+function send_action(verb, noun) {
+ // Reset action list here so we don't send more than one action per server prompt!
+ if (noun) {
+ if (game.actions && game.actions[verb] && game.actions[verb].includes(noun)) {
+ game.actions = null;
+ console.log("SEND ACTION", verb, noun);
+ socket.emit('action', verb, noun);
+ }
+ } else {
+ if (game.actions && game.actions[verb]) {
+ game.actions = null;
+ console.log("SEND ACTION", verb, noun);
+ socket.emit('action', verb);
+ }
+ }
+}
+
+function send_save() {
+ socket.emit('save');
+}
+
+function send_restore() {
+ let title = window.location.pathname.split("/")[1];
+ let save = window.localStorage[title + '/save'];
+ socket.emit('restore', window.localStorage[title + '/save']);
+}
+
+function send_restart(scenario) {
+ socket.emit('restart', scenario);
+}
diff --git a/public/common/grid.css b/public/common/grid.css
new file mode 100644
index 0000000..95a2ee4
--- /dev/null
+++ b/public/common/grid.css
@@ -0,0 +1,302 @@
+/* COMMON GRID LAYOUT */
+
+html, button, input, select {
+ font-family: "Source Sans", "Circled Numbers", "Dingbats", "Noto Emoji", "Verdana", sans-serif;
+ font-size: 16px;
+}
+
+.chat_text, #chat_input {
+ font-family: "Source Serif", "Circled Numbers", "Dingbats", "Noto Emoji", "Georgia", serif;
+}
+
+.log {
+ font-family: "Source Serif SmText", "Circled Numbers", "Dingbats", "Noto Emoji", "Georgia", serif;
+}
+
+html, body, div {
+ margin: 0;
+ padding: 0;
+}
+
+.status {
+ position:absolute;
+ z-index: 100;
+ left: 0;
+ bottom: 0;
+ background-color: white;
+ padding: 0 1ex;
+}
+
+.grid_window {
+ display: grid;
+ grid-template-columns: 1fr min-content;
+ grid-template-rows: min-content min-content 1fr;
+ gap: 0px;
+ width: 100vw;
+ height: 100vh;
+}
+
+.grid_window.hide_log .grid_role {
+ display: none;
+}
+.grid_window.hide_log .grid_log {
+ display: none;
+}
+
+.grid_center {
+ grid-column: 1;
+ grid-row: 2/4;
+ overflow: auto;
+ scrollbar-width: none;
+}
+
+.grid_role {
+ grid-column: 2;
+ grid-row: 2;
+ border-left: 1px solid black;
+ overflow-y: clip;
+ width: 209px;
+}
+
+.role_vp {
+ padding-top: 3px;
+ padding-right: 5px;
+ padding-left: 3px;
+ float: right;
+}
+
+.role_name {
+ padding-top: 3px;
+ padding-bottom: 3px;
+ padding-left: 25px;
+ text-indent: -20px;
+ border-bottom: 1px solid black;
+}
+
+.role .role_name::before { content: "\25cb "; opacity: 0.6; }
+.role.present .role_name::before { content: "\25cf "; opacity: 0.6; }
+
+.role_info {
+ border-bottom: 1px solid black;
+}
+
+.grid_log {
+ grid-column: 2;
+ grid-row: 3;
+ border-left: 1px solid black;
+ overflow-y: scroll;
+ scrollbar-width: thin;
+}
+
+.log {
+ padding-left: 20px;
+ padding-top: 8px;
+ padding-right: 8px;
+ text-indent: -12px;
+ font-size: 12px;
+ line-height: 18px;
+ white-space: pre-wrap;
+}
+
+.log > * {
+ min-height: 9px;
+}
+
+.grid_top {
+ grid-column: 1/3;
+ grid-row: 1;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid black;
+}
+
+.grid_top.disconnected {
+ background-color: red;
+}
+
+.menu {
+ user-select: none;
+}
+.menu_title img {
+ display: block;
+ height: 35px;
+ padding: 5px;
+}
+.menu:hover .menu_title {
+ background-color: black;
+ color: white;
+}
+.menu:hover .menu_title img {
+ filter: invert(100%);
+}
+.menu_popup {
+ display: none;
+ position: absolute;
+ min-width: 20ex;
+ white-space: nowrap;
+ border: 1px solid black;
+ background-color: white;
+ z-index: 100;
+}
+.menu:hover .menu_popup {
+ display: block;
+}
+.menu_separator {
+ border-top: 1px solid black;
+}
+.menu_item {
+ padding: 5px 10px;
+}
+.menu_item:hover {
+ background-color: black;
+ color: white;
+}
+
+.image_button {
+ user-select: none;
+}
+.image_button img {
+ display: block;
+ height: 35px;
+ padding: 5px;
+}
+.image_button:hover {
+ background-color: black;
+ color: white;
+}
+.image_button:hover img {
+ filter: invert(100%);
+}
+
+.grid_top button {
+ margin: 0 10px;
+}
+.grid_top button.hide {
+ display: none;
+}
+
+.prompt {
+ margin: 0 50px;
+ font-size: large;
+ flex-grow: 1;
+}
+
+.hand {
+ margin: 15px;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ min-height: 370px;
+}
+
+.card {
+ margin: 10px;
+ background-size: cover;
+ background-repeat: no-repeat;
+ transition: 100ms;
+ box-shadow: 1px 1px 5px rgba(0,0,0,0.5);
+ display: none;
+}
+.card.show {
+ display: block;
+}
+.card.enabled {
+ cursor: pointer;
+}
+.card.enabled:hover {
+ transform: scale(1.1);
+}
+.card.disabled {
+ filter: grayscale(100%);
+}
+
+.small_card {
+ margin: 15px;
+ background-size: cover;
+ background-repeat: no-repeat;
+ transition: 100ms;
+ box-shadow: 1px 1px 5px rgba(0,0,0,0.5);
+}
+.one .small_card:hover {
+ transform: scale(2.0) translate(0,35px);
+}
+.two .small_card:hover {
+ transform: scale(2.0) translate(0,-35px);
+}
+
+.map {
+ margin: 0 auto;
+ box-shadow: 0px 1px 10px rgba(0,0,0,0.5);
+}
+
+button {
+ font-size: 1rem;
+ margin: 0;
+ padding: 1px 12px;
+ background-color: gainsboro;
+}
+button:disabled {
+ color: gray;
+ border: 2px solid gainsboro;
+ outline: 1px solid gray;
+}
+button:enabled {
+ border: 2px outset white;
+ outline: 1px solid black;
+}
+button:enabled:active:hover {
+ border: 2px inset white;
+ padding: 2px 11px 0px 13px;
+}
+
+/* CHAT WINDOW */
+
+.chat_button.new {
+ filter: invert(100%);
+}
+.chat_window {
+ position: absolute;
+ left: 10px;
+ top: 55px;
+ width: 40rem;
+ z-index: 60;
+ border: 1px solid black;
+ background-color: white;
+ box-shadow: 0px 5px 10px 0px rgba(0,0,0,0.5);
+ visibility: hidden;
+ display: grid;
+ grid-template-rows: min-content 1fr min-content;
+}
+.chat_window.show {
+ visibility: visible;
+}
+.chat_header {
+ cursor: move;
+ background-color: gainsboro;
+ border-bottom: 1px solid black;
+ padding: 5px 10px;
+}
+.chat_text {
+ font-size: 16px;
+ line-height: 24px;
+ height: 216px;
+ padding: 0px 5px;
+ overflow-y: scroll;
+}
+.chat_text .date {
+ font-weight: bold;
+}
+.chat_form {
+ display: block;
+ margin: 0;
+ padding: 0;
+ border-top: 1px solid black;
+}
+#chat_input {
+ box-sizing: border-box;
+ width: 100%;
+ outline: none;
+ border: none;
+ padding: 5px;
+ font-size: 1rem;
+}
diff --git a/public/images/chat-bubble.svg b/public/images/chat-bubble.svg
new file mode 100644
index 0000000..1aa4ae9
--- /dev/null
+++ b/public/images/chat-bubble.svg
@@ -0,0 +1 @@
+<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M229.7 22.66A155.2 235.6 80.24 0 0 23.81 215.6 155.2 235.6 80.24 0 0 236.7 333.4c23.8 55.6-17.1 109.3-83.6 161.1 86.2-28.3 176.2-94.4 179.7-178.7a155.2 235.6 80.24 0 0 155.4-180.1A155.2 235.6 80.24 0 0 229.7 22.66z" fill="#000" fill-opacity="1"></path></g></svg> \ No newline at end of file
diff --git a/public/images/cog.svg b/public/images/cog.svg
new file mode 100644
index 0000000..6e44bd2
--- /dev/null
+++ b/public/images/cog.svg
@@ -0,0 +1 @@
+<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M234.875 18.78c-26.087 2.367-51.557 8.56-74.875 18.782 15.37 32.763 14.222 66.706-6.72 82.407-20.835 15.617-54.055 7.965-81.124-15.69-16.246 19.452-29.336 41.36-38.875 65.626 33.83 12.333 56.635 37.665 52.94 63.5-3.698 25.835-32.697 43.74-68.626 46.094 2.338 25.796 8.91 50.778 18.937 73.875 17.81-8.182 35.793-11.09 51.095-8.938 13.032 1.87 23.927 7.015 31.156 16.657 15.817 21.097 7.603 54.713-16.78 81.97 19.516 16.35 42.216 29.444 66.594 39.03 12.33-33.828 37.655-56.634 63.5-52.938 25.844 3.697 43.74 32.696 46.094 68.625 26.087-2.365 51.557-8.555 74.875-18.78-15.766-32.997-14.26-67.588 6.843-83.406 9.64-7.23 22.568-9.022 35.594-7.125 15.112 2.16 31.19 10.25 45.563 22.78 16.088-19.345 29.4-41.51 38.875-65.594-33.83-12.332-56.635-37.653-52.938-63.5 3.697-25.846 32.665-43.772 68.594-46.125-2.36-25.944-8.774-50.663-18.906-73.874-32.612 15.117-66.66 13.145-82.282-7.687-15.696-20.944-7.252-53.86 16.688-81-19.52-16.352-42.248-29.447-66.625-39.032-12.332 33.828-37.657 56.66-63.5 52.968-25.846-3.693-43.744-32.696-46.095-68.625zm21.656 95.126c79.626 0 144.376 64.752 144.376 144.375 0 79.626-64.75 144.376-144.375 144.376-79.624 0-144.374-64.75-144.374-144.375 0-79.624 64.75-144.374 144.375-144.374zm0 18.688c-69.524 0-125.686 56.162-125.686 125.687 0 69.526 56.162 125.69 125.687 125.69 69.526 0 125.69-56.164 125.69-125.69 0-69.522-56.164-125.686-125.69-125.686zm.033 15.125c61.094 0 110.625 49.53 110.625 110.624 0 61.095-49.53 110.625-110.625 110.625s-110.625-49.53-110.625-110.626c0-61.095 49.53-110.625 110.625-110.625z" fill="#000" fill-opacity="1"></path></g></svg> \ No newline at end of file
diff --git a/public/images/earth-africa-europe.svg b/public/images/earth-africa-europe.svg
new file mode 100644
index 0000000..39a4868
--- /dev/null
+++ b/public/images/earth-africa-europe.svg
@@ -0,0 +1 @@
+<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M256 32A224 224 0 0 0 32 256a224 224 0 0 0 224 224 224 224 0 0 0 223.672-217.45c-15.468 4.148-.306.22-16.467 4.51-9.1-2.965-28.42 10.264-29.197 10.264-1.756 0-14.65-49.66-14.65-49.66L381.08 208H368l-1.316 6.32 30.2 28.444-47.76 20.718-35.466-49.162-5.24 2.328 11.21 36.3 42.84 23.177-42.84 50.568 1.403 35.82-31.276 67.182-52.168-6.716-16-88.843-3.094-33.43-71.517-3.838-9.258-60.77 32.314-35.827 54.782-6.32 40.034 21.772 36.93-9.72c-1.112-18.374.05-12.54-1.112-18.374l-19.666-.7-1.053-10.184-5.97-2.107-2.81 12.64-9.48-.526-11.94-26.514-8.43 2.81 10.185 22.475-9.13 3.16-13.694-20.718-8.428-1.406-21.773 2.81-18.26 23.177-18.613 1.406-1.405-34.064 24.23-1.757 2.108-10.184-5.795-5.618-.174-3.69 16.182-7.94 11.56-2.944 19.665-6.848 6.32-7.375 3.512 7.375 22.474 2.81 8.78-9.13 5.97-24.582-6.673-2.81-11.238 16.857-8.43-9.834-11.586 14.75-14.4 2.458-.7-10.184 11.236-23.88 31.606-13.694 43.894-3.16 3.864 13.344s-4.916 13.344-5.97 13.695c-.138.045.428 1.754 1.464 4.565h31.195l14.047-11.59 29.484-9.138A224 224 0 0 0 256 32zM145.098 69.465l6.144 1.053s1.756 8.076.352 10.007c-1.405 1.932-10.885 7.903-10.885 7.903l-6.148-12.115 10.536-6.848zm41.086 11.588l8.252 19.49 3.337 16.68s-11.588 5.793-12.466 6.496c-.878.7-6.147-8.077-6.147-8.077l-1.93-17.91 2.81-15.1 6.144-1.58zm-12.116 22.474v16.155l-10.007 3.86-2.107-7.724 12.115-12.29zm189.104 238.618l-7.365 61.798-16.164 3.166s4.868-50.492 7.326-53.3l16.202-11.665z" fill="#000" fill-opacity="1"></path></g></svg> \ No newline at end of file
diff --git a/public/images/earth-america.svg b/public/images/earth-america.svg
new file mode 100644
index 0000000..56b30dc
--- /dev/null
+++ b/public/images/earth-america.svg
@@ -0,0 +1 @@
+<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M256 32c-37.764.086-74.894 9.72-107.938 28.002l27.52 19.36 40.033-13.694 24.582 5.62 8.78 49.864 15.1-11.588 41.087-14.046 18.26 27.742-35.82 18.963-22.473 16.152-2.458 22.475-24.932 21.07-7.023 34.064-14.047 1.053 7.023-38.63-53.027-2.807-12.64 18.61-.1-.01v26.644l25.824 1.986 23.838 16.885-1.986 25.328 33.77 5.96-.36.76 53.004-30.558 90.88 59.098-20.51 48.548-32.685 20.156-61.143 77.965-13.498-3.845L262.216 365l-42.213-42.213 7.853-13.86-25.732-9.482-25.326-30.79-21.853-4.967L116.422 208H112l-5.117 26.746-3.64-39.146 5.267-29.147-.7-23.178L97.247 98.2C55.516 140.12 32.06 196.847 32 256c0 123.712 100.288 224 224 224 82.413-.028 158.155-45.308 197.195-117.887L442.82 298.14l-5.62-36.17-34.06-27.392 6.67-37.926 15.803-20.367 37.555-5.05C428.766 87.086 346.913 32.072 256 32zm89.047 48H368v48l-32 16v-23.836zm-122.76 166.518l29.85 4.918-4.213 6.32-24.23-4.916z" fill="#000" fill-opacity="1"></path></g></svg> \ No newline at end of file
diff --git a/public/images/earth-asia-oceania.svg b/public/images/earth-asia-oceania.svg
new file mode 100644
index 0000000..dd9e6fb
--- /dev/null
+++ b/public/images/earth-asia-oceania.svg
@@ -0,0 +1 @@
+<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M256 32A224 224 0 0 0 99.596 95.68l11.12-2.66 38.24-13.41 44.198-8.44 17.383 17.38 27.81-15.892 32.778.498 5.96 7.946 48.17-10.43 73.005 22.35-33.77 13.904 8.442 34.763-16.885 10.925-10.43-52.144-22.844 1.987-3.476 25.824-23.838 9.933 2.48 33.77-14.897 2.484-10.926-19.37-5.463 15.397 12.416 22.844-39.73 40.725-3.973 12.414 6.457 17.878s-15.892 23.343-18.375 22.846c-2.484-.495-44.698-37.743-44.698-37.743l-26.32 15.395-20.86-31.288-18.375 1.49-24.83 69.032-17.88 4.967-38.454-52.088A224 224 0 0 0 256 480a224 224 0 0 0 224-224A224 224 0 0 0 256 32zm75.217 112.67l9.93 15.394-14.897 22.348-2.484 15.893-7.946-4.967h1.49l4.47-42.213 9.437-6.455zm-18.873 56.117l11.422 1.49-.993 11.422-29.796 5.462-.995-7.945 20.362-10.43zm-39.233 26.818l14.9 4.47-2.483 21.355-17.382-12.416 4.966-13.41zm-9.434 45.69l2.482 18.87H245.3l-.993-13.407 19.37-5.463zm39.728 3.476l33.772 8.94 16.885 20.858-45.193-11.918-11.918 4.965-19.866-21.85 26.32-.994zM198.12 289.685l27.316 8.443 33.77 17.38s-20.86 4.47-23.84 3.477c-2.98-.993-43.702-19.37-43.702-19.37l6.457-9.93zm116.21 10.43l10.43 24.83 22.347 13.906-4.966 34.763-25.326 23.342-26.322-21.355-27.314 8.94-19.367 13.903L230.9 369.64s8.442-26.82 10.428-27.812l19.37-5.96 8.443-19.366 7.448 12.912 25.326-6.457 12.416-22.844zm86.91 49.66l4.965 2.484-10.926 15.396-3.475-8.94 9.435-8.94zm-11.422 19.867s6.456 4.47 4.47 5.96c-1.987 1.49-27.81 19.367-27.81 19.367l-10.927-5.463 23.343-17.38 10.925-2.483zm-82.44 32.28l12.415.498-10.428 9.932-1.988-10.43z" fill="#000" fill-opacity="1"></path></g></svg> \ No newline at end of file
diff --git a/public/images/scroll-quill.svg b/public/images/scroll-quill.svg
new file mode 100644
index 0000000..19891eb
--- /dev/null
+++ b/public/images/scroll-quill.svg
@@ -0,0 +1 @@
+<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><g class="" style="" transform="translate(0,0)"><path d="M311.9 47.95c-17.6 0-34.6.7-50.7 2.43L244.6 93.5l-4.9-40.04c-2.5.46-5 .94-7.5 1.47-9.1 1.94-15.1 7.22-20.3 14.87-5.2 7.65-8.9 17.5-12.1 26.6C191 121.5 184 148 178.4 175c6 5.1 12 10.3 17.9 15.4l30.7-17.6 33.8 26.1 51.9-19.7 61 24.5-6.8 16.7-54.4-21.8-54.7 20.7-32.2-24.9-14.9 8.5c19.6 17.3 38.6 34.4 56.5 51.2l14-6.4 33.9 16.1 31.2-13.1 24.2 23.3-12.4 13-15.8-15.1-27.6 11.7-33-15.8c6.9 6.7 13.6 13.2 20.1 19.7l1.7 1.8 19.5 76.3-7.8-5.7-53 .4-38.1-17.8-42.4 14.6-5.8-17 49.2-17 41.1 19.2 24.7-.2-70.7-51.7c-19.7 4.6-39.4 2.8-58.1-3.7-4.2 44.4-5.9 85.7-7 118.7-.4 10.7 2.7 23 7.5 32.5 4.9 9.5 11.7 15.4 15 16.1 5.2 1.2 19 3.2 37.7 5.1l12.4-39 19.1 41.7c16.7 1.2 35 2 53.5 2.2 28.2.3 57.1-.9 82-4.7 15.8-2.3 29.6-6 40.7-10.4-11.8-5.1-21.6-10.6-29.1-16.6-11.1-8.9-18.2-19.3-17.3-30.9v.2c5.4-96.4 10.8-188.8 30.3-286l.1-.4.1-.4c5.3-17.9 17.9-39.86 36.1-55.83-13.9-2.06-28.6-4-43.7-5.66l-22.3 25.3-2.2-27.7c-19-1.64-38.4-2.71-57.4-2.92h-5.7zm148.5 20.44c-4.7 3.69-9.2 8.03-13.3 12.73 12.1 8.18 21.4 23.38 21.8 36.98.3 7.8-1.9 14.9-7.7 21.4-5.8 6.4-15.6 12.4-31.6 15.8l3.8 17.6c18.6-4 32.3-11.5 41.2-21.4 9-9.9 12.7-22.2 12.3-34-.6-19.3-11.1-37.59-26.5-49.11zM25.44 71.91c-.24 1.61-.38 3.43-.38 5.62.1 7.69 2.03 18.17 5.83 30.17 3.41 10.7 8.27 22.5 14.35 34.8 10.63-5.3 20.59-11 28.41-18.1-4.42 12.5-10.15 24.7-18.6 36.5 4.14 7.2 8.63 14.4 13.45 21.5 10.64-5.3 20.72-13 29.52-26.1-3.3 16-8.47 30.6-18.27 41.8 6.53 8.5 13.5 16.8 20.75 24.5 8.7-9.3 15.6-21 20.7-34.9 3.8 18.5 2.6 35.3-5.7 49.4 8 7.2 16.3 13.7 24.8 19.1 6.1-14 8.9-30.6 8.5-49.7 9.2 23.7 11.3 42.9 9.6 59.5 20.2 9.2 40.8 12 61.3 6.1l4.2-1.3 69.3 50.6-5.9-22.8c-73-72.8-175.4-156.7-261.86-226.69zM312.8 123.9l33.2 13.8 31.3-9.9 5.4 17.2-37.5 11.9-33.6-14-28.8 8.1-4.8-17.4zm107.3 236.2c-.7 0-1.3.1-2 .1-3.5.1-7.2.5-11.1 1.3l3.4 17.6c12.2-2.3 20-.4 24.5 2.5 4.4 2.9 6.3 6.8 6.4 12.5.1 9.3-7 23-23.3 32.5 5.4 2.9 11.9 5.9 19.3 8.7 14.4-11.6 22.1-26.8 22-41.4-.1-10.7-5.2-21.2-14.6-27.4-6.7-4.3-15-6.5-24.6-6.4z" fill="#000" fill-opacity="1"></path></g></svg> \ No newline at end of file