Add game.id's and adds stage attribute to x-present

Chris Pollett [2023-01-27 20:Jan:th]
Add game.id's and adds stage attribute to x-present
Filename
js/game.js
diff --git a/js/game.js b/js/game.js
index b86fdea..5c2445f 100644
--- a/js/game.js
+++ b/js/game.js
@@ -259,9 +259,17 @@ function makeGameObject(dom_object)
                 }
                 let check = child.getAttribute("ck");
                 if (!check) {
-                    check = "";
+                    let check = child.getAttribute("check");
+                    if (!check) {
+                        check = "";
+                    }
+                }
+                let stage = child.getAttribute("stage");
+                if (!stage) {
+                    stage = "";
                 }
-                game_object[attribute_name].push([check, child.innerHTML]);
+                game_object[attribute_name].push([check, stage,
+                child.innerHTML]);
             } else {
                 game_object[attribute_name] = child.innerHTML;
             }
@@ -305,9 +313,6 @@ function toggleMainNav()
 {
     let game_content = elt('game-content');
     let nav_obj = elt('main-nav');
-    if (!nav_obj) {
-        return;
-    }
     if ((!nav_obj.style.left && !nav_obj.style.right) ||
         nav_obj.style.left == '0px' || nav_obj.style.right == '0px') {
         game_content.style.width = "calc(100% - 40px)";
@@ -500,12 +505,12 @@ class Location
         game_content.scrollTop = 0;
         game_content.scrollLeft = 0;
         for (let section of this.present) {
-            if (!section[1]) {
+            if (!section[2]) {
                 continue;
             }
             let [check_result, proceed, pause] =
-                this.evaluateCheckCondition(section[0]);
-            let prepared_section = this.prepareSection(section[1]);
+                this.evaluateCheckConditionStaging(section[0], section[1]);
+            let prepared_section = this.prepareSection(section[2]);
             if (check_result) {
                 if (proceed) {
                     let old_inner_html = game_content.innerHTML;
@@ -581,48 +586,49 @@ class Location
     /**
      * Evaluates the condition from a ck attribute ofan x-present tag.
      *
-     * @param {string} condition contents from a ck attribute.
-     *  Conditions can be boolean conditions on game variable, delay conditions,
-     *  or clickProceed condition.
+     * @param {string} condition contents from a ck or check attribute.
+     *  Conditions can be boolean conditions on game variable.
+     * @param {string} staging contents from a stage attribute.
+     *      If none empty, such an attribute could have a sequence of
+     *      pause(some_millisecond); and clickProceed(some_string) commands
      * @return {Array} [check_result, proceed, pause] if the condition involved
      *  a boolean expression, then check_result will hold the result of
      *  the expression (so the caller then could prevent the the display of
      *  an x-present tag if false), proceed is the link text (if any) for a
-     *  link if the condition involved a clickProceed (which is supposed to
-     *  delay the presentation of the x-present tag until after the user
-     *  clicks the link), pause (if non zero) is the number of miliseconds
-     *  to sleep before presenting the x-pressent tag according to the condition
+     *  link for the first clickProceed (which is supposed to delay the
+     *  presentation of the x-present tag until after the user clicks the
+     *  link) if found (else ""), pause (if non zero) is the number of
+     *  miliseconds to sleep before presenting the x-pressent tag according to
+     *  the condition
      */
-    evaluateCheckCondition(condition)
+    evaluateCheckConditionStaging(condition, staging)
     {
-        let check;
         let proceed = "";
         let pause = 0;
-        if (condition == "") {
-            check = "";
-        } else {
-            check = condition;
-            let old_check = "";
-            while (old_check != check) {
-                old_check = check;
-                let click_pattern =
-                    /clickProceed\([\'\"]([^)]+)[\'\"]\);?/;
-                let click_match = check.match(click_pattern);
-                if (click_match) {
-                    proceed = click_match[1];
-                    check = "";
-                    break;
-                }
-                let pause_pattern = /sleep\(([^)]+)\);?/;
-                let pause_match = check.match(pause_pattern);
-                if (pause_match) {
-                    pause += parseInt(pause_match[1]);
-                }
-                check = check.replace(pause_pattern, "");
+        condition = (typeof condition == "string") ? condition : "";
+        let check_result = (condition.replace(/\s+/, "") != "") ?
+            eval(condition) : true;
+        if (typeof check_result != "boolean") {
+            check_result = false;
+            console.log(condition + " didn't evaluate to a boolean");
+        }
+        let staging_remainder = staging;
+        let old_staging = "";
+        while (check_result && old_staging != staging_remainder) {
+            old_staging = staging_remainder;
+            let click_pattern = /clickProceed\([\'\"]([^)]+)[\'\"]\);?/;
+            let click_match = staging_remainder.match(click_pattern);
+            if (click_match) {
+                proceed = click_match[1];
+                break;
+            }
+            let pause_pattern = /pause\(([^)]+)\);?/;
+            let pause_match = staging_remainder.match(pause_pattern);
+            if (pause_match) {
+                pause += parseInt(pause_match[1]);
             }
+            staging_remainder = staging_remainder.replace(pause_pattern, "");
         }
-        let check_result = (check.replace(/\s+/, "") != "") ? eval(check)
-            : true;
         return [check_result, proceed, pause];
     }
     /**
@@ -688,6 +694,13 @@ class Location
  */
 class Game
 {
+    /**
+     * A semi-unique identifier for this particular game to try to ensure
+     * two different games hosted in the same folder don't collide in
+     * sessionStorage.
+     * @type {string}
+     */
+    id;
     /**
      * Current date followed by a space followedby the current time of
      * the most recent game capture. Used in providing a description of
@@ -757,6 +770,18 @@ class Game
      */
     constructor()
     {
+        let game_elt = tag('x-game')[0];
+        let doc_length = 0;
+        let middle_five = "";
+        if (game_elt) {
+            doc_length = game_elt.innerHTML.length;
+            if (doc_length > 8) {
+                middle_five = game_elt.innerHTML.slice(
+                    Math.floor(doc_length/2), 5);
+            }
+        }
+        // a semi-unique code for this particular game
+        this.id = encodeURI(middle_five + doc_length);
         this.reset();
     }
     /**
@@ -1005,7 +1030,7 @@ class Game
         this.future_history.push(current_state);
         let previous_game_state = this.history.pop();
         this.restoreState(previous_game_state);
-        sessionStorage.current = previous_game_state;
+        sessionStorage["current" + this.id] = previous_game_state;
         this.describeMainCharacterLocation();
         if (this.history.length == 0) {
             elt('previous-history').disabled = true;
@@ -1028,7 +1053,7 @@ class Game
         this.history.push(current_state);
         let next_game_state = this.future_history.pop();
         this.restoreState(next_game_state);
-        sessionStorage.current = next_game_state;
+        sessionStorage["current" + this.id] = next_game_state;
         this.describeMainCharacterLocation();
         if (this.future_history.length == 0) {
             elt('next-history').disabled = true;
@@ -1048,7 +1073,8 @@ class Game
             let slot_matches = field.match(/^slot(\d+)/);
             if (slot_matches && slot_matches[1]) {
                 let slot_number = parseInt(slot_matches[1]);
-                let game_save = sessionStorage.getItem("slot" + slot_number);
+                let game_save = sessionStorage.getItem("slot" + game.id
+                    + slot_number);
                 if (game_save) {
                     let game_state = JSON.parse(game_save);
                     saves_location["slot" + slot_number] =
@@ -1080,17 +1106,17 @@ class Game
     {
         slot_number = parseInt(slot_number);
         let saves_location = game.locations['saves'];
-        let game_state = sessionStorage.getItem("slot" + slot_number);
+        let game_state = sessionStorage.getItem("slot" + game.id + slot_number);
         if (game_state) {
             this.clearHistory();
-            sessionStorage.current = game_state;
+            sessionStorage["current" + game.id] = game_state;
             this.restoreState(game_state);
         } else {
             let save_state = this.captureState();
             game_state = this.history[this.history.length - 1];
             this.restoreState(game_state);
             saves_location['filename' + slot_number] =  this.timestamp;
-            sessionStorage.setItem("slot" + slot_number, game_state);
+            sessionStorage.setItem("slot" + game.id + slot_number, game_state);
             this.restoreState(save_state);
             this.evaluateAction(saves_location['default-action']);
         }
@@ -1108,7 +1134,7 @@ class Game
     {
         slot_number = parseInt(slot_number);
         let saves_location = game.locations['saves'];
-        sessionStorage.removeItem("slot" + slot_number);
+        sessionStorage.removeItem("slot" + game.id + slot_number);
         saves_location['filled' + slot_number] = "not-filled";
         saves_location['delete' + slot_number] = "disabled";
         saves_location['slot' + slot_number] = "Save";
@@ -1146,7 +1172,7 @@ class Game
                 file_reader.addEventListener('load', (load_event) => {
                     let game_state = load_event.target.result;
                     this.clearHistory();
-                    sessionStorage.current = game_state;
+                    sessionStorage["current" + this.id] = game_state;
                     this.restoreState(game_state);
                     game.describeMainCharacterLocation();
                 });
@@ -1203,8 +1229,8 @@ class Game
             }
         }
         let new_game_state;
-        if (sessionStorage.current) {
-            new_game_state = sessionStorage.current;
+        if (sessionStorage["current" + game.id]) {
+            new_game_state = sessionStorage["current" + game.id];
         }
         if (!this.moveMainCharacter(hash)) {
             return;
@@ -1213,12 +1239,12 @@ class Game
         if (this.hasNavBar) {
             elt('next-history').disabled = true;
         }
-        if (sessionStorage.current) {
+        if (sessionStorage["current" + game.id]) {
             this.history.push(new_game_state);
         }
         this.evaluateDefaultActions(this.objects);
         this.evaluateDefaultActions(this.locations);
-        sessionStorage.current = this.captureState();
+        sessionStorage["current" + game.id] = this.captureState();
         this.describeMainCharacterLocation();
         if (this.hasNavBar) {
             if (this.history.length == 0) {
@@ -1353,8 +1379,8 @@ class Game
 async function initGame()
 {
     game = new Game();
-    if (sessionStorage.current) {
-        game.restoreState(sessionStorage.current);
+    if (sessionStorage["current" + game.id]) {
+        game.restoreState(sessionStorage["current" + game.id]);
     }
     game.takeTurn("");
     game.clearHistory();
ViewGit