Pre-release
Adventure.js Docs Downloads
Score: 0 Moves: 0
// RestoreManager.js
(function () {
  /*global adventurejs A aria*/
  "use strict";

  /**
   *
   * @class adventurejs.RestoreManager
   * @ajsinternal
   * @ajsnavheading FrameworkReference
   * @param {Game} game A reference to the game instance.
   * @summary Manages the process of restoring saved games.
   * @todo Restore from adventurejs.com web server.
   * @classdesc
   * <p>
   * <strong>RestoreManager</strong> manages the job of restoring
   * saved games. It contains all the methods needed to create the
   * Restore pop-up screen. RestoreManager can restore a game from
   * a local save file, from browser cookies, or from the
   * <a href="https://adventurejs.com">adventurejs.com</a>
   * web server.
   * </p>
   * <p>
   * RestoreManager is created automatically
   * by {@link adventurejs.Game|Game}. This is an internal class that
   * authors should not need to construct or modify. However, if
   * you'd like to try, you can find styles for the Save & Restore
   * pop-ups in <a href="/css/adventurejs.css">adventurejs.css</a>.
   * All relevant styles are prefixed with '.save_' or '.restore_'.
   * </p>
   */
  class RestoreManager {
    constructor(game) {
      /**
       * A reference back to the main {@link adventurejs.Game|Game} object.
       * @var {Object} adventurejs.RestoreManager#game
       * @default {}
       */
      this.game = game;

      /**
       * Collection of HTML elements: the tabs used to navigate
       * between the different restore methods.
       * @var {Array} adventurejs.RestoreManager#restoreTabs
       * @default []
       */
      this.restoreTabs = [];

      /**
       * Collection of HTML elements: the panes containing the
       * different restore methods.
       * @var {Array} adventurejs.RestoreManager#restorePanes
       * @default []
       */
      this.restorePanes = [];

      /**
       * Collection of HTML elements: the action buttons for
       * the different restore methods.
       * @var {Array} adventurejs.RestoreManager#restoreButtons
       * @default []
       */
      this.restoreButtons = [];

      /**
       * Div element to contain the Restore pop-up.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreDisplay
       * @default {}
       */
      this.restoreDisplay = document.createElement("div");
      this.restoreDisplay.classList.add("ajs-restore-display");
      this.game.display.displayEl.appendChild(this.restoreDisplay);

      this.restoreOuter = document.createElement("div");
      this.restoreOuter.classList.add("restore_outer");
      this.restoreDisplay.appendChild(this.restoreOuter);

      this.restoreInner = document.createElement("div");
      this.restoreInner.classList.add("restore_inner");
      this.restoreOuter.appendChild(this.restoreInner);

      // -----------------
      // CLOSE BUTTON
      // -----------------

      /**
       * Button element to close the Restore pop-up.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreClose
       * @default {}
       */
      this.restoreClose = document.createElement("button");
      this.restoreClose.classList.add("restore_close", "close_button");
      this.restoreClose.type = "button";
      this.restoreClose.value = "Close";
      this.restoreClose.innerHTML = "X";
      this.restoreClose.name = "restoreClose";
      this.restoreClose.manager = this;
      this.restoreInner.appendChild(this.restoreClose);
      this.restoreClose.addEventListener("click", function () {
        this.manager.clickButton_Close();
      });

      // -----------------
      // TITLE BAR
      // -----------------
      /**
       * P element to close the Restore pop-up's title bar.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreDisplayTitle
       * @default {}
       */
      this.restoreDisplayTitle = document.createElement("p");
      this.restoreDisplayTitle.classList.add("restore_display_title");
      this.restoreDisplayTitle.innerHTML = "RESTORE GAME";
      this.restoreInner.appendChild(this.restoreDisplayTitle);

      // -----------------
      // TABS
      // -----------------
      /**
       * Div element to contain the Restore pop-up's tabs.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreRowTabs
       * @default {}
       */
      this.restoreRowTabs = document.createElement("div");
      this.restoreRowTabs.classList.add("restore_row_tabs");
      this.restoreInner.appendChild(this.restoreRowTabs);

      // -----------------
      // FILE TAB
      // -----------------
      /**
       * Button element to navigate to 'Restore from File' option.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreTab_File
       * @default {}
       */
      this.restoreTab_File = document.createElement("button");
      this.restoreTab_File.classList.add(
        "restore_tab_file",
        "active",
        "restore_tab"
      );
      this.restoreTab_File.type = "button";
      this.restoreTab_File.value = "Restore from File";
      this.restoreTab_File.innerHTML = "Restore from File";
      this.restoreTab_File.name = "restoreTab_File";
      this.restoreTab_File.manager = this;
      this.restoreRowTabs.appendChild(this.restoreTab_File);
      this.restoreTab_File.addEventListener("click", function () {
        if (false === this.classList.contains("active")) {
          this.manager.selectTab(this);
        }
      });
      this.restoreTabs.push(this.restoreTab_File);

      // -----------------
      // BROWSER TAB
      // -----------------
      /**
       * Button element to navigate to 'Restore from Browser' option.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreTab_Browser
       * @default {}
       */
      this.restoreTab_Browser = document.createElement("button");
      this.restoreTab_Browser.classList.add(
        "restore_tab_browser",
        "restore_tab"
      );
      this.restoreTab_Browser.type = "button";
      this.restoreTab_Browser.value = "Restore from Browser";
      this.restoreTab_Browser.innerHTML = "Restore from Browser";
      this.restoreTab_Browser.name = "restoreTab_Browser";
      this.restoreTab_Browser.manager = this;
      this.restoreRowTabs.appendChild(this.restoreTab_Browser);
      this.restoreTab_Browser.addEventListener("click", function () {
        if (false === this.classList.contains("active")) {
          this.manager.selectTab(this);
          this.manager.getLocalStorageList();
        }
      });
      this.restoreTabs.push(this.restoreTab_Browser);

      // -----------------
      // SERVER TAB
      // -----------------
      /**
       * Button element to navigate to 'Restore from Server' option.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreTab_Server
       * @default {}
       */
      this.restoreTab_Server = document.createElement("button");
      this.restoreTab_Server.classList.add("restore_tab_server", "restore_tab");
      this.restoreTab_Server.type = "button";
      this.restoreTab_Server.value = "Restore from Server";
      this.restoreTab_Server.innerHTML = "Restore from Server";
      this.restoreTab_Server.name = "restoreTab_Server";
      this.restoreTab_Server.manager = this;
      this.restoreRowTabs.appendChild(this.restoreTab_Server);
      this.restoreTab_Server.addEventListener("click", function () {
        if (false === this.classList.contains("active")) {
          this.manager.selectTab(this);
        }
      });
      this.restoreTabs.push(this.restoreTab_Server);

      // -----------------
      // PANES
      // -----------------
      /**
       * Div element to contain Restore panes.
       * @var {HTMLElement} adventurejs.RestoreManager#restoreRowPanes
       * @default {}
       */
      this.restoreRowPanes = document.createElement("div");
      this.restoreRowPanes.classList.add("restore_row_panes");
      this.restoreInner.appendChild(this.restoreRowPanes);

      // -----------------
      // FILE PANE
      // -----------------
      /**
       * Div element to contain 'Restore from File' pane.
       * @var {HTMLElement} adventurejs.RestoreManager#restorePane_File
       * @default {}
       */
      this.restorePane_File = document.createElement("div");
      this.restorePane_File.classList.add(
        "restore_pane_file",
        "restore_pane",
        "active"
      );
      this.restorePane_File.innerHTML += "";
      this.restorePane_File.name = "restorePane_File";
      this.restorePane_File.manager = this;
      this.restorePanes.push(this.restorePane_File);

      // check that this browser supports the methods
      // needed for save/restore of local files
      this.fileReaderUnsupported =
        "undefined" === typeof window.File ||
        "undefined" === typeof window.FileReader ||
        "undefined" === typeof window.FileList ||
        "undefined" === typeof window.Blob;
      var a = document.createElement("a");
      this.downloadUnsupported = "undefined" === typeof a.download;
      //document.removeChild(a);

      if (this.fileReaderUnsupported || this.downloadUnsupported) {
        this.restorePane_File.classList.add("unsupported_feature");
      }

      this.restorePane_File_QA = document.createElement("div");
      this.restorePane_File_QA.classList = "restore_pane_qa";
      this.restorePane_File.appendChild(this.restorePane_File_QA);
      this.restorePane_File_QA.innerHTML =
        "" +
        "<p class='restore_pane_qa_q'>What's this?</p>" +
        "<p class='restore_pane_qa_a'>" +
        "Use this option to restore a save you downloaded using the " +
        '<span class="bootstrap_blue ">Save to File</span> ' +
        "option. If, when you saved, your browser didn't offer " +
        "you a file dialog to choose your download location, " +
        "then your save file was probably downloaded to " +
        "your computer's default Download folder. " +
        "</p>";
      this.restorePane_File_QA.addEventListener("click", function () {
        this.classList.toggle("active");
      });

      this.restoreRowPanes.appendChild(this.restorePane_File);
      this.restoreTab_File.restorePane = this.restorePane_File;
      this.restoreTab_File.restorePaneQA = this.restorePane_File_QA;

      // CHOOSE FILE

      this.restoreInputContainer_File = document.createElement("div");
      this.restoreInputContainer_File.classList.add(
        "restore_input_container_file",
        "restore_input_container",
        "supported"
      );
      this.restorePane_File.appendChild(this.restoreInputContainer_File);

      this.restoreInput_File = document.createElement("input");
      this.restoreInput_File.classList.add(
        "restore_input_file",
        "restore_input"
      );
      this.restoreInputContainer_File.appendChild(this.restoreInput_File);
      this.restoreInput_File.type = "file";
      this.restoreInput_File.accept = ".json";
      this.restoreInput_File.name = "restoreInput_File";
      this.restoreInput_File.manager = this;
      this.restoreInput_File.id = this.game.game_name + "_restoreInput_File";

      this.restoreInput_File.addEventListener(
        "change",
        function (e) {
          console.log("restoreInput_File.click");
          console.log("--", e.target.files[0]);
          console.log("--", e.target.value);

          // we've set our file input to accept=".json", but just in case
          if (".json" !== e.target.value.substring(e.target.value.length - 5)) {
            e.target.value = "";
            alert("Selected file is not a JSON file.");
            return false;
          }
          this.manager.restoreButton_File.classList.remove("inactive");
        },
        false
      );

      // https://tympanus.net/codrops/2015/09/15/styling-customizing-file-inputs-smart-way/
      // method for styling inputs - but content to keep native style for now
      // this.restoreLabelFile = document.createElement("label");
      // this.restoreLabelFile.classList.add( "restore_label_file", "restore_label" );
      // this.restoreLabelFile.setAttribute("for", this.game.game_name + "_restoreInput_File" );
      // this.restoreLabelFile.innerHTML = "Choose a file";
      // this.restoreInputContainer_File.appendChild( this.restoreLabelFile );

      // RESTORE BUTTON

      this.restoreButtonContainer_File = document.createElement("div");
      this.restoreButtonContainer_File.classList.add(
        "restore_button_container_file",
        "restore_button_container",
        "supported"
      );
      this.restorePane_File.appendChild(this.restoreButtonContainer_File);

      this.restoreButton_File = document.createElement("button");
      this.restoreButton_File.classList.add(
        "restore_button_file",
        "restore_button",
        "inactive"
      );
      this.restoreButton_File.type = "button";
      this.restoreButton_File.value = "Restore from File";
      this.restoreButton_File.innerHTML = "Restore from File";
      this.restoreButton_File.name = "restoreButton_File";
      this.restoreButton_File.manager = this;

      this.restoreButtonContainer_File.appendChild(this.restoreButton_File);
      this.restoreButton_File.addEventListener("click", function () {
        this.manager.clickRestoreButton_File();
      });

      this.restoreButtons.push(this.restoreButton_File);

      // UNSUPPORTED MESSAGE

      this.restoreWarningContainer_File = document.createElement("div");
      this.restoreWarningContainer_File.classList.add(
        "restore_warning_container_file",
        "restore_warning_container",
        "unsupported"
      );
      this.restorePane_File.appendChild(this.restoreWarningContainer_File);

      this.restoreWarning_File = document.createElement("div");
      this.restoreWarning_File.classList =
        "restore_pane_warning alert alert-danger";
      this.restoreWarningContainer_File.appendChild(this.restoreWarning_File);
      this.restoreWarning_File.innerHTML =
        "" +
        "<p class='restore_pane_warning'>" +
        "Unfortunately, your web browser doesn't appear to support " +
        "this save/restore method. Please try one of the other methods, " +
        "or try playing with a different web browser. " +
        "</p>";

      // -----------------
      // BROWSER PANE
      // -----------------
      /**
       * Div element to contain 'Restore from Browser' pane.
       * @var {HTMLElement} adventurejs.RestoreManager#restorePane_Browser
       * @default {}
       */
      this.restorePane_Browser = document.createElement("div");
      this.restorePane_Browser.classList.add(
        "restore_pane_browser",
        "restore_pane"
      );
      this.restorePane_Browser.name = "restorePane_Browser";
      this.restorePane_Browser.manager = this;
      this.restorePanes.push(this.restorePane_Browser);

      this.restorePane_Browser_QA = document.createElement("div");
      this.restorePane_Browser_QA.classList = "restore_pane_qa";
      this.restorePane_Browser.appendChild(this.restorePane_Browser_QA);
      this.restorePane_Browser_QA.innerHTML =
        "" +
        "<p class='restore_pane_qa_q'>What's this?</p>" +
        "<p class='restore_pane_qa_a'>" +
        "Use this option to restore a save you downloaded using the " +
        '<span class="bootstrap_blue ">Save to Browser</span> ' +
        "option. It will only find saves you made while playing the game" +
        "on this browser on this computer at this web domain. " +
        "</p>";
      this.restorePane_Browser_QA.addEventListener("click", function () {
        this.classList.toggle("active");
      });

      this.restoreRowPanes.appendChild(this.restorePane_Browser);
      this.restoreTab_Browser.restorePane = this.restorePane_Browser;
      this.restoreTab_Browser.restorePaneQA = this.restorePane_Browser_QA;

      // LISTBOX
      // example with aria tagging
      // https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox-scrollable.html

      // instantiate a new listbox
      // var exListbox = new aria.Listbox(document.getElementById('ss_elem_list'));

      this.restoreListboxContainer_Browser = document.createElement("div");
      this.restoreListboxContainer_Browser.classList.add(
        "restore_listbox_container_browser",
        "restore_listbox_container"
      );
      this.restorePane_Browser.appendChild(
        this.restoreListboxContainer_Browser
      );

      this.restoreListboxLabel_Browser = document.createElement("p");
      this.restoreListboxLabel_Browser.classList.add(
        "restore_listbox_label_browser",
        "restore_listbox_label",
        "listbox_label"
      );
      this.restoreListboxContainer_Browser.appendChild(
        this.restoreListboxLabel_Browser
      );
      this.restoreListboxLabel_Browser.innerHTML =
        "Select a saved game to restore:";
      this.restoreListboxLabel_Browser.id =
        this.game.game_name + "_restoreListboxLabel_Browser";

      this.restoreListbox_Browser = document.createElement("ul");
      this.restoreListbox_Browser.classList.add(
        "restore_listbox_browser",
        "restore_listbox",
        "listbox"
      );
      this.restoreListboxContainer_Browser.appendChild(
        this.restoreListbox_Browser
      );
      this.restoreListbox_Browser.id =
        this.game.game_name + "_restoreListbox_Browser";
      this.restoreListbox_Browser.setAttribute(
        "aria-labelledby",
        this.restoreListboxLabel_Browser.id
      );
      this.restoreListbox_Browser.role = "listbox";
      this.restoreListbox_Browser.setAttribute("tabindex", "0");

      this.restoreObserver_Browser = new MutationObserver(function (mutation) {
        if (mutation[0].attributeName !== "aria-activedescendant") {
          return;
        }
        var selection = mutation[0].target.getAttribute(
          mutation[0].attributeName
        );
        //if( document.getElementById( selection ).classList.contains( "focused" ) ) {
        if (
          null !==
          document.getElementById(selection).getAttribute("aria-selected")
        ) {
          console.log(selection);
          this.manager.restoreButton_Browser.classList.remove("inactive");
        }
      });
      this.restoreObserver_Browser.manager = this;

      this.restoreObserver_Browser.observe(this.restoreListbox_Browser, {
        // childList: true,
        attributes: true,
      });

      // RESTORE BUTTON

      this.restoreButtonContainer_Browser = document.createElement("div");
      this.restoreButtonContainer_Browser.classList.add(
        "restore_button_container_browser",
        "restore_button_container"
      );
      this.restorePane_Browser.appendChild(this.restoreButtonContainer_Browser);

      this.restoreButton_Browser = document.createElement("button");
      this.restoreButton_Browser.classList.add(
        "restore_button_browser",
        "restore_button"
      );
      this.restoreButton_Browser.type = "button";
      this.restoreButton_Browser.value = "Restore from Browser";
      this.restoreButton_Browser.innerHTML = "Restore from Browser";
      this.restoreButton_Browser.name = "restoreButton_Browser";
      this.restoreButton_Browser.manager = this;

      this.restoreButtonContainer_Browser.appendChild(
        this.restoreButton_Browser
      );
      this.restoreButton_Browser.addEventListener("click", function () {
        this.manager.clickRestoreButton_Browser();
      });

      this.restoreButtons.push(this.restoreButton_Browser);

      // -----------------
      // SERVER PANE
      // -----------------
      /**
       * Div element to contain 'Restore from Server' pane.
       * @var {HTMLElement} adventurejs.RestoreManager#restorePane_Server
       * @default {}
       */
      this.restorePane_Server = document.createElement("div");
      this.restorePane_Server.classList.add(
        "restore_pane_server",
        "restore_pane"
      );
      this.restorePane_Server.name = "restorePane_Server";
      this.restorePane_Server.manager = this;
      this.restorePanes.push(this.restorePane_Server);

      this.restorePane_Server_QA = document.createElement("div");
      this.restorePane_Server_QA.classList = "restore_pane_qa";
      this.restorePane_Server.appendChild(this.restorePane_Server_QA);
      this.restorePane_Server_QA.innerHTML =
        "" +
        "<p class='restore_pane_qa_q'>What's this?</p>" +
        "<p class='restore_pane_qa_a'>" +
        "Use this option to restore a game you saved using the " +
        '<span class="bootstrap_blue ">Save to Server</span> ' +
        "option. You will need to log in to the adventurejs server " +
        "if you haven't already. " +
        "</p>";
      this.restorePane_Server_QA.addEventListener("click", function () {
        this.classList.toggle("active");
      });
      this.restoreRowPanes.appendChild(this.restorePane_Server);
      this.restoreTab_Server.restorePane = this.restorePane_Server;
      this.restoreTab_Server.restorePaneQA = this.restorePane_Server_QA;

      // RESTORE BUTTON

      this.restoreButtonContainer_Server = document.createElement("div");
      this.restoreButtonContainer_Server.classList.add(
        "restore_button_container_server",
        "restore_button_container"
      );
      this.restorePane_Server.appendChild(this.restoreButtonContainer_Server);

      this.restoreButton_Server = document.createElement("button");
      this.restoreButton_Server.classList.add(
        "restore_button_server",
        "restore_button"
      );
      this.restoreButton_Server.type = "button";
      this.restoreButton_Server.value = "Restore from Server";
      this.restoreButton_Server.innerHTML = "Restore from Server";
      this.restoreButton_Server.name = "restoreButton_Server";
      this.restoreButton_Server.manager = this;

      this.restoreButtonContainer_Server.appendChild(this.restoreButton_Server);
      this.restoreButton_Server.addEventListener("click", function () {
        this.manager.clickRestoreButton_Server();
      });

      this.restoreButtons.push(this.restoreButton_Server);
    }

    /**
     * Open the Restore pop-up window.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#openDisplay
     * @kind function
     */
    openDisplay() {
      this.resetDisplay();
      this.restoreDisplay.classList.add("active");
    }

    /**
     * Reset the Restore pop-up window.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#resetDisplay
     * @kind function
     */
    resetDisplay() {
      this.restoreInput_File.value = "";
      this.restoreButton_File.classList.add("inactive");
      this.restoreButton_Browser.classList.add("inactive");
      this.restoreButton_Server.classList.add("inactive");
    }

    /**
     * Close the Restore pop-up window.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#closeDisplay
     * @kind function
     */
    closeDisplay() {
      console.warn("RestoreManager.js closeDisplay");
      this.restoreDisplay.classList.remove("active");
      this.selectTab(this.restoreTab_File);
      this.restoreButtons.forEach(function (button) {
        button.classList.add("inactive");
      });
    }

    /**
     * Function that gets called by the Close button.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#clickButton_Close
     * @kind function
     */
    clickButton_Close() {
      console.log("clickButton_Close");
      this.closeDisplay();
      var msg = "Restore cancelled.";
      this.game.print(msg);
      return;
    }

    // -----------------
    // FILE PANE
    // -----------------

    /**
     * Restore the seleted save file.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#clickRestoreButton_File
     * @kind function
     */
    clickRestoreButton_File() {
      console.log("clickRestoreButton_File");
      var fileReader = new FileReader();
      // forward closured fileReader.onload event
      // to a more useful scope
      fileReader.onload = (function (manager) {
        return function (e) {
          manager.fileReaderOnload(e);
        };
      })(this);
      fileReader.readAsText(this.restoreInput_File.files[0], "UTF-8");
    }

    /**
     * Complete the restore operation and close the pop-up.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#fileReaderOnload
     * @kind function
     */
    fileReaderOnload(e) {
      console.log("fileReaderOnload");
      console.log(e);

      this.closeDisplay();
      var restored = A.restoreWorld.call(this.game, e.target.result);

      if (false === restored) {
        var msg = "Restore failed!";
        this.game.print(msg);
        return false;
      }

      var msg = "Game restored.";
      this.game.print(msg);
      return;
    }

    // -----------------
    // BROWSER PANE
    // -----------------
    // https://blog.logrocket.com/the-complete-guide-to-using-localstorage-in-javascript-apps-ba44edb53a36/

    /**
     * Restore the seleted save file.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#clickRestoreButton_Browser
     * @kind function
     */
    clickRestoreButton_Browser() {
      console.log("clickRestoreButton_Browser");

      var saveName = this.restoreListbox_Browser.getAttribute(
        "aria-activedescendant"
      );

      var restored = this.game.restoreWorld(window.localStorage[saveName]);

      this.closeDisplay();

      if (false === restored) {
        var msg = "Restore failed!";
        this.game.print(msg);
        return false;
      }

      var msg = "Game restored.";
      this.game.print(msg);
      return;
    }

    /**
     * Get a list of saved games stored in browser cookies.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#getLocalStorageList
     * @kind function
     */
    getLocalStorageList() {
      this.resetLocalStorageListBox();

      var storage = window.localStorage;
      for (var key in storage) {
        // if( this.game.titleSerialized !== key.substr( 0, this.game.titleSerialized.length ) ) // @deprecated
        if (
          this.game.titleSerialized !==
          key.substring(0, this.game.titleSerialized.length)
        ) {
          continue;
        }
        var li = document.createElement("li");
        li.id = key;
        //li.innerHTML = key;
        li.innerHTML = key.substring(this.game.titleSerialized.length + 1);

        li.setAttribute("role", "option");
        this.restoreListbox_Browser.appendChild(li);
      }
      if (0 < this.restoreListbox_Browser.childNodes.length) {
        this.restoreListbox_Browser.setAttribute(
          "aria-activedescendant",
          this.restoreListbox_Browser.childNodes[0].id
        );
      }
      this.restoreListboxInstance_Browser = new aria.Listbox(
        this.restoreListbox_Browser
      );
      this.restoreListbox_Browser.focus();
      // TODO why doesn't this focus work?
    }

    /**
     * Reset local storage listbox.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#resetLocalStorageListBox
     * @kind function
     */
    resetLocalStorageListBox() {
      if (
        !this.restoreListbox_Browser.innerHTML &&
        "undefined" === typeof this.restoreListboxInstance_Browser
      ) {
        // already empty
        return;
      }
      if ("undefined" !== typeof this.restoreListboxInstance_Browser) {
        delete this.restoreListboxInstance_Browser;
      }
      for (
        var i = this.restoreListbox_Browser.childNodes.length - 1;
        i > -1;
        i--
      ) {
        // TODO remove? or repurpose?
        this.restoreListbox_Browser.removeChild(
          this.restoreListbox_Browser.childNodes[i]
        );
      }
      this.restoreListbox_Browser.innerHTML = "";
      return;
    }

    // -----------------
    // SERVER PANE
    // -----------------

    /**
     * Restore the seleted save file.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#clickRestoreButton_Server
     * @kind function
     */
    clickRestoreButton_Server() {
      console.log("clickRestoreButton_Server");
    }

    /**
     * Make a selected tab active.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#selectTab
     * @kind function
     */
    selectTab(selectedTab) {
      this.restoreTabs.forEach(function (tabButton) {
        if (tabButton !== selectedTab) {
          tabButton.classList.remove("active");
          tabButton.restorePane.classList.remove("active");
          tabButton.restorePaneQA.classList.remove("active");
        } else {
          tabButton.classList.add("active");
          tabButton.restorePane.classList.add("active");
        }
      }, selectedTab);
    }

    /**
     * Provides a chainable shortcut method for setting a number of properties on the instance.
     * @memberOf adventurejs.RestoreManager
     * @method adventurejs.RestoreManager#set
     * @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
     * @returns {adventurejs.RestoreManager} Returns the instance the method is called on (useful for chaining calls.)
     * @chainable
     */
    set(props) {
      // if (props != null) {
      //   for (var n in props) {
      //     this[n] = props[n];
      //   }
      // }
      // return this;
      return A.deepSet.call(this.game, props, this);
    }
  }
  adventurejs.RestoreManager = RestoreManager;
})();