Made CSS file part of project, made subfolders for js and css

Chris Pollett [2023-01-15 05:Jan:th]
Made CSS file part of project, made subfolders for js and css
Filename
css/game.css
game.js
diff --git a/css/game.css b/css/game.css
new file mode 100644
index 0000000..9153331
--- /dev/null
+++ b/css/game.css
@@ -0,0 +1,262 @@
+/**
+ * FRISE (FRee Interactive Story Engine)
+ * A light-weight engine for writing interactive fiction and games.
+ *
+ * Copyright 2022-2023 Christopher Pollett chris@pollett.org
+ *
+ * @license
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * @file The CSS code used to style the elements used by a game
+ * @author Chris Pollett
+ * @link https://www.frise.org/
+ * @copyright 2022 - 2023
+ */
+x-game,
+x-action
+{
+    display: none;
+}
+.float-right
+{
+    float: right;
+}
+.float-left
+{
+    float: left;
+}
+.left
+{
+    text-align: left;
+}
+.right
+{
+    text-align: right;
+}
+.center
+{
+    text-align: center;
+}
+.fit-content
+{
+    width: fit-content;
+}
+.rounded
+{
+    border-radius: 20px;
+}
+.rounded-top
+{
+    border-radius: 20px 20px 0 0;
+}
+.medium-width
+{
+    width: 150px;
+}
+.mobile .medium-width
+{
+    width: 100px;
+}
+.medium-border
+{
+    border: solid 2px black;
+}
+#game-content
+{
+    font-family: "Oswald", serif;
+    font-size: 18pt;
+    height: 100%;
+    left: 300px;
+    line-height: 1.6;
+    overflow-x: scroll;
+    overflow-y: scroll;
+    position: fixed;
+    transition: left .25s ease-in;
+    top: 0px;
+    padding-top: 23px;
+    width: calc(100% - 300px);
+}
+#main-nav,
+#main-bar
+{
+    background: linear-gradient(.25turn, white, 97%, lightgray);
+    height: 100%;
+    left: 0;
+    overflow-y: scroll;
+    position: fixed;
+    text-align: center;
+    transition: left .25s ease-in;
+    top: 0px;
+    width: 240px;
+    z-index: 0;
+}
+#main-bar
+{
+    background: white;
+    height: 50px;
+    text-align: left;
+    top: 0;
+    width: 50px;
+    z-index: 2;
+}
+.mobile #main-bar
+{
+    background: white;
+    height: 50px;
+    text-align: left;
+    top: 0;
+    width: 50px;
+    z-index: 2;
+}
+#main-nav button,
+#main-bar button
+{
+    border-radius: 10px;
+    font-size: 21pt;
+    padding: 2px 5px 5px 5px;
+    width: 40px;
+}
+.mobile #main-nav button,
+.mobile #main-bar button
+{
+    font-size: 18pt;
+    height: 50px;
+    margin: 2px;
+    padding: 8px 10px 10px 10px;
+    width: 40px;
+}
+#main-nav h1
+{
+    margin: 8px;
+}
+#main-nav x-button,
+#main-nav input[type="range"]
+{
+    width: 170px;
+}
+#main-nav .nav-label
+{
+    font-size: 16pt;
+    margin: auto;
+    text-align:left;
+    width: 170px;
+}
+.filled
+{
+    background-color: blue;
+    color: white;
+}
+.float-right
+{
+    float: right;
+}
+table.save-table
+{
+    width: 7in;
+}
+table.save-table,
+table.save-table tr,
+table.save-table th,
+table.save-table td
+{
+    border-collapse: collapse;
+    border: 1px solid black;
+    padding:10px;
+    text-align: center;
+}
+table.save-table td.save-name
+{
+    width: 3in;
+}
+h1
+{
+    margin: 0px;
+    padding: 0px;
+}
+x-speaker
+{
+    border: 3px solid black;
+    border-radius: 10px;
+    display: block;
+    font-size:18pt;
+    margin: 10px;
+    min-height: 110px;
+    padding: 10px;
+    width: 90%;
+}
+.mobile x-speaker
+{
+    margin: 2px;
+    padding: 4px;
+    width: 82%;
+}
+x-speaker figure:first-of-type
+{
+    border-radius: 5px;
+    display: block;
+    float: left;
+    margin: 0;
+    width: 120px;
+}
+.mobile x-speaker figure:first-of-type
+{
+    width: 80px;
+}
+x-speaker figure:first-of-type > img
+{
+    border-radius: 10px;
+    display: block;
+    height: 100px;
+    margin: auto;
+    width: 100px;
+}
+.mobile x-speaker figure:first-of-type > img
+{
+    width: 70px;
+    height: 70px;
+}
+.footer-space {
+    height: 1in;
+}
+x-button
+{
+    background-color: #F0F0F6;
+    border: 1px solid gray;
+    border-radius: 5px;
+    color: black;
+    display: inline-block;
+    font-size: 18pt;
+    font-weight: bold;
+    padding: 8px;
+    margin: 3px;
+}
+x-button.disabled
+{
+    border: 1px solid lightgray;
+    background-color: #F6F6FA;
+    color: #666;
+    cursor: not-allowed;
+}
+x-button:hover
+{
+    background-color: lightgray;
+}
+
+x-button.disabled:hover
+{
+    color: #666;
+    background-color: #F6F6FA;
+}
+img
+{
+    max-width: 90%;
+}
+input
+{
+    border: 2px solid lightblue;
+    border-radius: 5px;
+    font-size: 18pt;
+    padding: 2px;
+}
diff --git a/game.js b/js/game.js
similarity index 91%
rename from game.js
rename to js/game.js
index 85acdec..12d65ea 100644
--- a/game.js
+++ b/js/game.js
@@ -1,17 +1,20 @@
 /**
- * Copyright 2022-2023 Christopher Pollett
+ * FRISE (FRee Interactive Story Engine)
+ * A light-weight engine for writing interactive fiction and games.
+ *
+ * Copyright 2022-2023 Christopher Pollett chris@pollett.org
  *
+ * @license
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-/**
- * FRISE (FRee Interactive Story Engine)
- *
- * A light-weight engine for writing interactive fiction and games.
  *
+ * @file The JS code used to manage a game
+ * @author Chris Pollett
+ * @link https://www.frise.org/
+ * @copyright 2022 - 2023
  */
-/**
+/*
  * Global Variables
  */
 /**
@@ -188,18 +191,28 @@ function upperFirst(str)
  */
 var object_counter = 0;
 /**
- * Used to convert a DOM Element dom_object to an Object or Location sutiable
+ * Used to convert a DOM Element dom_object to an Object or Location suitable
  * for a FRISE game. dom_object's whose tagName does not begin with x-
  * will result in null being returned. If the tagName is x-location, a
- * Location object will be returned otherwise a Javascript Object is returned.
+ * Location object will be returned, otherwise, a Javascript Object is returned.
  * The innerHTML of any subtag of an x-object or an
- * x-location beginning x- become the value of a field in the resulting
- * object with name the name of of the tag less x-. For example,
- * <x-object>
- *
+ * x-location beginning with x- becomes the value of a field in the resulting
+ * object with  the name of the tag less x-. For example, a DOM_object
+ * representing the following HTML code:
+ * <x-object id="bob">
+ *   <x-name>Robert Smith</x-name>
+ *   <x-age>25</x-age>
  * </x-object>
- * @param {Element}
- * @return {Object?|Location?}
+ * will be processed to a Javascript Object
+ * {
+ *   id: "bob",
+ *   name: "Robert Smith",
+ *   age: "25"
+ * }
+ * @param {Element} DOMElement to be convert into a FRISE game Object or
+ *  Location
+ * @return {Object?|Location?} the resulting FRISE game Object or Location or
+ *  null if the tagName of the DOMElement didn't begin with x-
  */
 function makeGameObject(dom_object)
 {
@@ -279,30 +292,32 @@ function toggleDisplay(id, display_type)
     }
 }
 /**
- *
+ * Used to toggle the display or non-display of the main navigation bar
+ * on the side of game screen
  */
-function toggleOptions(elt_id, stop_pos)
+function toggleMainNav()
 {
     let game_content = elt('game-content');
-    let elt_obj = elt(elt_id);
-    if ((!elt_obj.style.left && !elt_obj.style.right) ||
-        elt_obj.style.left == '0px' || elt_obj.style.right == '0px') {
-        elt_obj.style.left = (stop_pos - 300) + 'px';
-        if (is_mobile) {
-            elt_obj.style.width = "240px";
-        }
+    let nav_obj = elt('main-nav');
+    if ((!nav_obj.style.left && !nav_obj.style.right) ||
+        nav_obj.style.left == '0px' || nav_obj.style.right == '0px') {
         game_content.style.left = "55px";
         game_content.style.width = "calc(100% - 40px)";
-        elt_obj.style.backgroundColor = 'white';
+        nav_obj.style.left = '-300px';
+        if (is_mobile) {
+            nav_obj.style.width = "240px";
+            game_content.style.width = "calc(100% - 70px)";
+        }
+        nav_obj.style.backgroundColor = 'white';
     } else {
-        elt_obj.style.left = '0px';
+        nav_obj.style.left = '0px';
         game_content.style.left = "300px";
         game_content.style.width = "calc(100% - 480px)";
         if (is_mobile) {
-            elt_obj.style.width = "100%";
+            nav_obj.style.width = "100%";
             game_content.style.left = "100%";
         }
-        elt_obj.style.backgroundColor = 'lightgray';
+        nav_obj.style.backgroundColor = 'lightgray';
     }
 }
 /**
@@ -329,7 +344,7 @@ function addListenersAnchors(anchors)
                 if (!anchor.classList.contains('disabled')) {
                     game.takeTurn(hash);
                     if (call_toggle) {
-                        toggleOptions('main-nav', 0);
+                        toggleMainNav();
                     }
                 }
                 event.preventDefault();
@@ -382,7 +397,7 @@ function enableSavesAndInventory()
     }
 }
 /**
- *
+ *
  */
 function interpolateVariables(text)
 {
@@ -438,6 +453,8 @@ class Location
         disableSavesAndInventory();
         let game_content = elt("game-content");
         game_content.innerHTML = "";
+        game_content.scrollTop = 0;
+        game_content.scrollLeft = 0;
         for (let section of this.present) {
             if (!section[1]) {
                 continue;
@@ -468,25 +485,31 @@ class Location
      */
     prepareControls()
     {
-        let game_content = elt("game-content");
-        let input_fields = game_content.querySelectorAll("input");
-        for (const input_field of input_fields) {
-            let target_object = null;
-            let target_name = input_field.getAttribute("data-for");
-            if (typeof target_name != "undefined") {
-                if (game.objects[target_name]) {
-                    target_object = game.objects[target_name];
-                } else if (game.locations[target_name]) {
-                    target_object = game.locations[target_name];
-                }
-                if (target_object) {
-                    let target_field = input_field.getAttribute("name");
-                    if (target_field) {
-                        input_field.value = target_object[target_field];
-                        input_field.addEventListener("input", (evt) =>
-                        {
-                            target_object[target_field] = input_field.value;
-                        });
+        const content_areas = ["main-nav", "game-content"];
+        for (const content_area of content_areas) {
+            let content = elt(content_area);
+            let input_fields = content.querySelectorAll("input");
+            for (const input_field of input_fields) {
+                let target_object = null;
+                let target_name = input_field.getAttribute("data-for");
+                if (typeof target_name != "undefined") {
+                    if (game.objects[target_name]) {
+                        target_object = game.objects[target_name];
+                    } else if (game.locations[target_name]) {
+                        target_object = game.locations[target_name];
+                    }
+                    if (target_object) {
+                        let target_field = input_field.getAttribute("name");
+                        if (target_field) {
+                            input_field.value = target_object[target_field];
+                            if(!input_field.disabled) {
+                                input_field.addEventListener("input", (evt) =>
+                                {
+                                    target_object[target_field] =
+                                        input_field.value;
+                                });
+                            }
+                        }
                     }
                 }
             }
@@ -663,7 +686,7 @@ class Game
             </div>
             <div id="game-content"></div>`;
         elt('main-toggle').onclick = (evt) => {
-            toggleOptions('main-nav', 0);
+            toggleMainNav('main-nav', 0);
         };
         elt('next-history').onclick = (evt) => {
             this.nextHistory();
@@ -687,7 +710,7 @@ class Game
         let head = tag("head")[0];
         head.innerHTML += `<meta name="viewport" `+
             `content="width=device-width, initial-scale=1.0" >`
-        toggleOptions('main-nav', 0);
+        toggleMainNav();
     }
     /**
      *
@@ -921,7 +944,7 @@ class Game
         saves_location['filename' + slot_number] =  "...";
     }
     /**
-     *
+     * Deletes all game saves from sessionStorage
      */
     deleteSlotAll()
     {
@@ -1039,7 +1062,9 @@ class Game
         return true;
     }
     /**
+     * Moves the main character according to the provided url fragment.
      *
+     * @param {string} hash a url fragment as described above
      */
     moveMainCharacter(hash)
     {
@@ -1119,7 +1144,10 @@ class Game
 }
 /**
  * Module initialization function, used to set up the game object corresponding
- * to the current HTML document.
+ * to the current HTML document. If there is a current game state in
+ * sessionStorage it is used to initialize the game state, otherwise,
+ * the game state is based on the start of the game. After this state is
+ * set up, the current location is drawn to the game content area.
  */
 async function initGame()
 {
ViewGit