diff options
| -rw-r--r-- | fg21sim/webui/static/js/configs.js | 262 | ||||
| -rw-r--r-- | fg21sim/webui/static/js/websocket.js | 51 | 
2 files changed, 221 insertions, 92 deletions
| diff --git a/fg21sim/webui/static/js/configs.js b/fg21sim/webui/static/js/configs.js index b39a43f..bf7bcc5 100644 --- a/fg21sim/webui/static/js/configs.js +++ b/fg21sim/webui/static/js/configs.js @@ -169,13 +169,16 @@ var getFormConfigAll = function () {    var names = $("#conf-form").find("input[name]").map(      function () { return $(this).attr("name"); }).get();    names = $.unique(names); -  console.log("Collected", names.length, "configurations items");    var data = {}; -  $.each(names, function (i, name) { +  names.forEach(function (name) {      data[name] = getFormConfigSingle(name);    });    // Do not forget the "userconfig"    data["userconfig"] = getFormConfigSingle("userconfig"); +  // Delete unwanted items +  ["workdir", "configfile", "_xsrf"].forEach(function (name) { +    delete data[name]; +  });    console.log("Collected form configurations data:", data);    return data;  }; @@ -260,7 +263,10 @@ var setFormConfigs = function (data, errors) {  /**   * Update the configuration form status indicator: "#conf-status" - * Also store the validity status in a custom data attribute. + * + * NOTE: + * Also store the current validity status in a custom data attribute: + * `validity`, which has a boolean value.   */  var updateFormConfigStatus = function () {    var target = $("#conf-status"); @@ -291,94 +297,266 @@ var updateFormConfigStatus = function () {  /** - * Reset the server-side configurations to the defaults. + * Compose the notification contents and shown in the "#modal-configs" + * modal box.   * - * @param {Object} ws - The opened WebSocket object, through which send - *                      the request for configuration defaults. + * The input `data` may have the following attributes: + *   - `icon` : FontAwesome icon (specified without the beginning `fa-`) + *   - `message` : Main summary message + *   - `code` : Error code if it is an error notification + *   - `reason` : Reason of the error   */ -var resetServerConfigs = function (ws) { -  var msg = {type: "configs", action: "reset"}; -  ws.send(JSON.stringify(msg)); +var showConfigsModal = function (data) { +  var modalBox = $("#modal-configs"); +  modalBox.html(""); +  var p1 = $("<p>"); +  if (data.icon) { +    $("<span>").addClass("fa fa-2x").addClass("fa-" + data.icon).appendTo(p1); +  } +  if (data.message) { +    $("<span>").text(" " + data.message).appendTo(p1); +  } +  modalBox.append(p1); +  if (data.code) { +    modalBox.append($("<p>Error Code: </p>") +                    .append($("<span>") +                            .addClass("label label-warning") +                            .text(data.code))); +  } +  if (data.reason) { +    modalBox.append($("<p>Reason: </p>") +                    .append($("<span>") +                            .addClass("label label-warning") +                            .text(data.reason))); +  } +  // Show the modal box +  modalBox.modal();  };  /** - * Get the configurations from the server. - * When the response arrived, the bound function will take appropriate - * reactions (e.g., `setConfigForm()`) to update the form contents. + * Get the configurations from the server and update the client form + * to the newly received values. + * + * NOTE: + * The configurations are not validated on the server, therefore, + * there is no validation error returned. + * For the validation, see function `validateServerConfigs()`.   * + * @param {String} url - The URL that handles the "configs" AJAX requests.   * @param {Array} [keys=null] - List of keys whose values will be requested.   *                              If `null` then request all configurations. - *   */ -var getServerConfigs = function (ws, keys) { +var getServerConfigs = function (url, keys) {    keys = typeof keys !== "undefined" ? keys : null; -  var msg = {type: "configs", action: "get", keys: keys}; -  ws.send(JSON.stringify(msg)); +  return $.getJSON(url, {action: "get", keys: JSON.stringify(keys)}, +                   function (response) { +                     setFormConfigs(response.data, {}); +                   }); +}; + + +/** + * Validate the server-side configurations to get the validation errors, + * and mark the corresponding form fields to be invalid with details. + */ +var validateServerConfigs = function (url) { +  return $.getJSON(url, {action: "validate"}, +                   function (response) { +                     setFormConfigs({}, response.errors); +                   }); +}; + + +/** + * Reset the server-side configurations to the defaults, then sync back to + * the client-side form configurations. + */ +var resetConfigs = function (url) { +  $.postJSON(url, {action: "reset"}) +    .done(function () { +      // Server-side configurations already reset +      resetFormConfigs(); +      // Sync server-side configurations back to the client +      $.when(getServerConfigs(url), +             validateServerConfigs(url)) +        .done(function () { +          // Update the configuration status label +          updateFormConfigStatus(); +          // Popup a modal notification +          var modalData = {}; +          modalData.icon = "check-circle"; +          modalData.message = "Reset and synchronized the configurations."; +          showConfigsModal(modalData); +        }); +    }) +    .fail(function (error) { +      var modalData = {}; +      modalData.icon = "times-circle"; +      modalData.message = "Failed to reset the configurations!"; +      modalData.code = error.status; +      modalData.reason = error.statusText; +      showConfigsModal(modalData); +    });  };  /**   * Set the server-side configurations using the sent data from the client.   * - * NOTE: The server will validate the values and further check the whole - *       configurations, and response the config options with invalid values. + * NOTE: + * The supplied configuration data are validated on the server side, and + * the validation errors are sent back. + * However, the whole configurations is NOT checked, therefore, function + * `validateServerConfigs()` should be used if necessary.   *   * @param {Object} [data={}] - Group of key-value pairs that to be sent to   *                             the server to update the configurations there.   */ -var setServerConfigs = function (ws, data) { +var setServerConfigs = function (url, data) {    data = typeof data !== "undefined" ? data : {}; -  var msg = {type: "configs", action: "set", data: data}; -  ws.send(JSON.stringify(msg)); +  return $.postJSON(url, {action: "reset", data: data}, +                    function (response) { +                      setFormConfigs({}, response.errors); +                    }) +    .fail(function (error) { +      var modalData = {}; +      modalData.icon = "times-circle"; +      modalData.message = "Failed to update/set the configuration data!"; +      modalData.code = error.status; +      modalData.reason = error.statusText; +      showConfigsModal(modalData); +    });  };  /** - * Request the server side configurations with user configuration file merged. - * When the response arrived, the bound function will delegate an appropriate - * function (i.e., `setConfigForm()`) to update the form contents. + * Request the server to load/merge the configurations from the specified + * user configuration file.   *   * @param {Object} userconfig - Absolute path to the user config file on the   *                              server. If not specified, then determine from   *                              the form fields "workdir" and "configfile".   */ -var loadServerConfigFile = function (ws, userconfig) { -  if (typeof userconfig === "undefined") { +var loadServerConfigFile = function (url, userconfig) { +  if (! userconfig) {      userconfig = getFormConfigSingle("userconfig");    } -  var msg = {type: "configs", action: "load", userconfig: userconfig}; -  ws.send(JSON.stringify(msg)); +  return $.postJSON(url, {action: "load", userconfig: userconfig}) +    .fail(function (error) { +      var modalData = {}; +      modalData.icon = "times-circle"; +      modalData.message = "Failed to load the user configuration file!"; +      modalData.code = error.status; +      modalData.reason = error.statusText; +      showConfigsModal(modalData); +    });  };  /** - * Request the server side configurations with user configuration file merged. - * When the response arrived, the bound function will delegate an appropriate - * function (i.e., `setConfigForm()`) to update the form contents. + * Request the server to save current configurations to the supplied output + * file.   *   * @param {Boolean} [clobber=false] - Whether overwrite the existing file.   */ -var saveServerConfigFile = function (ws, clobber) { +var saveServerConfigFile = function (url, clobber) {    clobber = typeof clobber !== "undefined" ? clobber : false;    var userconfig = getFormConfigSingle("userconfig"); -  var msg = {type: "configs", -             action: "save", -             outfile: userconfig, -             clobber: clobber}; -  ws.send(JSON.stringify(msg)); +  var data = {action: "save", +              outfile: userconfig, +              clobber: clobber}; +  return $.postJSON(url, data) +    .fail(function (error) { +      var modalData = {}; +      modalData.icon = "times-circle"; +      modalData.message = "Failed to save the configurations!"; +      modalData.code = error.status; +      modalData.reason = error.statusText; +      showConfigsModal(modalData); +    });  };  /** - * Handle the received message of type "configs" + * Handle the received message of type "configs" pushed through the WebSocket   */ -var handleMsgConfigs = function (msg) { -  if (msg.success) { +var handleWebSocketMsgConfigs = function (msg) { +  if (msg.action === "push") { +    // Pushed configurations (with validations) of current state on the server      setFormConfigs(msg.data, msg.errors);      updateFormConfigStatus();    } else { -    console.error("WebSocket 'configs' request failed with error:", msg.error); -    // TODO: add error code support and handle each specific error ... +    console.warn("WebSocket: received message:", msg);    }  }; + + +$(document).ready(function () { +  // URL to handle the "configs" AJAX requests +  var ajax_url = "/ajax/configs"; + +  // Re-check/validate the whole form configurations +  $("#conf-recheck").on("click", function () { +    var data = getFormConfigAll(); +    setServerConfigs(ajax_url, data) +      .then(function () { validateServerConfigs(ajax_url); }) +      .done(function () { updateFormConfigStatus(); }); +  }); + +  // Reset both server-side and client-side configurations to the defaults +  $("#reset-defaults").on("click", function () { +    // TODO: add a confirmation dialog +    resetConfigs(ajax_url); +  }); + +  // Load the configurations from the specified user configuration file +  $("#load-configfile").on("click", function () { +    var userconfig = getFormConfigSingle("userconfig"); +    resetFormConfigs(); +    $.when(loadServerConfigFile(ajax_url, userconfig), +           getServerConfigs(ajax_url), +           validateServerConfigs(ajax_url)) +      .done(function () { +        // Update the configuration status label +        updateFormConfigStatus(); +        // Popup a modal notification +        var modalData = {}; +        modalData.icon = "check-circle"; +        modalData.message = "Loaded the configurations from file."; +        showConfigsModal(modalData); +      }); +  }); + +  // Save the current configurations to file +  $("#save-configfile").on("click", function () { +    // TODO: add a confirmation on overwrite +    saveServerConfigFile(ajax_url, true) +      .done(function () { +        var modalData = {}; +        if ($("#conf-status").data("validity")) { +          // Form configurations is valid :) +          modalData.icon = "check-circle"; +          modalData.message = "Configurations saved to file."; +        } else { +          // Configurations is currently invalid! +          modalData.icon = "warning"; +          modalData.message = ("Configurations saved to file. " + +                               "There exist some invalid values!"); +        } +        showConfigsModal(modalData); +      }); +  }); + +  // Sync changed field to server, validate and update form +  $("#conf-form input").on("change", function (e) { +    console.log("Element changed:", e); +    var name = $(e.target).attr("name"); +    var value = getFormConfigSingle(name); +    // Synchronize the changed form configuration to the server +    // NOTE: Use the "computed property names" available in ECMAScript 6 +    setServerConfigs(ajax_url, {[name]: value}) +      .then(function () { validateServerConfigs(ajax_url); }) +      .done(function () { updateFormConfigStatus(); }); +  }); +}); diff --git a/fg21sim/webui/static/js/websocket.js b/fg21sim/webui/static/js/websocket.js index e446380..a8f9085 100644 --- a/fg21sim/webui/static/js/websocket.js +++ b/fg21sim/webui/static/js/websocket.js @@ -132,7 +132,7 @@ var connectWebSocket = function (url) {      console.log(msg);      // Delegate appropriate actions to handle the received message      if (msg.type === "configs") { -      handleMsgConfigs(msg); +      handleWebSocketMsgConfigs(msg);      }      else if (msg.type === "console") {        handleMsgConsole(msg); @@ -173,55 +173,6 @@ $(document).ready(function () {      });      /********************************************************************** -     * Configuration form -     */ - -    // Re-check/validate the whole form configurations -    $("#conf-recheck").on("click", function () { -      var data = getFormConfigAll(); -      setServerConfigs(g_ws, data); -    }); - -    // Reset the configurations to the defaults -    $("#reset-defaults").on("click", function () { -      // TODO: -      // * add a confirmation dialog; -      // * add pop up to indicate success/fail -      resetFormConfigs(); -      resetServerConfigs(g_ws); -      getServerConfigs(g_ws); -    }); - -    // Load the configurations from the specified user configuration file -    $("#load-configfile").on("click", function () { -      // TODO: -      // * add pop up to indicate success/fail -      var userconfig = getFormConfigSingle("userconfig"); -      resetFormConfigs(); -      loadServerConfigFile(g_ws, userconfig); -      getServerConfigs(g_ws); -    }); - -    // Save the current configurations to file -    $("#save-configfile").on("click", function () { -      // TODO: -      // * validate the whole configurations before save -      // * add a confirmation on overwrite -      // * add pop up to indicate success/fail -      saveServerConfigFile(g_ws, true);  // clobber=true -    }); - -    // Sync changed field to server, validate and update form -    $("#conf-form input").on("change", function (e) { -      console.log("Element changed:", e); -      var name = $(e.target).attr("name"); -      var value = getFormConfigSingle(name); -      // Sync form configuration to the server -      // NOTE: Use the "computed property names" available in ECMAScript 6 -      setServerConfigs(g_ws, {[name]: value}); -    }); - -    /**********************************************************************       * Console operations       */ | 
