<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Monitor password</title>
    <style>
      input, button {
        min-height: 10mm;
        min-width: 10mm;
      }
    </style>
    <script>

      var service_name = "monitor-password",  // XXX hardcoded
        monitor_json_url = "/monitor.haljson",
        status_json_url = "/public/" + service_name + ".status.json",
        rerun_cgi_url = "/cgi-bin/monitor-run-promise.cgi?service=" + service_name,
        password_cgi_url_part = "/cgi-bin/monitor-password.cgi?password=";

      function newDeferred() {
        var d = {
          "promise": undefined,
          "resolve": undefined,
          "reject": undefined
        };
        d.promise = new Promise(function (resolve, reject) {
          d.resolve = resolve;
          d.reject = reject;
        });
        return d;
      }

      function xhr(param) {
        /*global XMLHttpRequest */
        var d = newDeferred(), xhr = new XMLHttpRequest(), k, i, l, a;
        d.promise.cancel = function () { xhr.abort(); };
        if (param.username) {
          xhr.open((param.method || "GET").toUpperCase(), param.url, true, param.username, param.password);
        } else {
          xhr.open((param.method || "GET").toUpperCase(), param.url, true);
        }
        xhr.responseType = param.responseType || "";
        if (param.withCredentials !== undefined) {
          xhr.withCredentials = param.withCredentials;
        }
        if (param.headers) {
          a = Object.keys(param.headers);
          l = a.length;
          for (i = 0; i < l; i += 1) {
            k = a[i];
            xhr.setRequestHeader(k, param.headers[k]);
          }
        }
        xhr.addEventListener("load", function (e) {
          var r, t = e.target, callback;
          if (param.noStatusCheck) {
            d.resolve(t);
          } else if (t.status < 400) {
            d.resolve(t);
          } else {
            r = new Error("HTTP: " + (t.status ? t.status + " " : "") + (t.statusText || "Unknown"));
            r.target = t;
            d.reject(r);
          }
        }, false);
        xhr.addEventListener("error", function (e) {
          return d.reject(new Error("HTTP: Error"));
        }, false);
        xhr.addEventListener("abort", function (e) {
          return d.reject(new Error("HTTP: Aborted"));
        }, false);
        xhr.send(param.data);
        return d.promise;
      }

      function unexpectedError(reason) {
        console.error(reason);
        alert(reason);
      }

      function MonitorPasswordInterface(config) {
        var it = this,
          statusP = document.createElement("p"),
          descriptionP = document.createElement("p"),
          form = document.createElement("form"),
          formPassword1Input = document.createElement("input"),
          formPassword2Input = document.createElement("input"),
          formChangePasswordButton = document.createElement("button"),
          errorH2 = document.createElement("h2"),
          errorPre = document.createElement("pre"),
          header = document.createElement("header"),
          h1 = document.createElement("h1"),
          h2 = document.createElement("h2"),
          a = document.createElement("a"),
          button = document.createElement("button");

        this.element = config.rootElement || document.createElement("div");
        this.statusP = statusP;
        this.descriptionP = descriptionP;
        this.formPassword1Input = formPassword1Input;
        this.formPassword2Input = formPassword2Input;
        this.formChangePasswordButton = formChangePasswordButton;
        this.errorH2 = errorH2;
        this.errorPre = errorPre;

        this.element.appendChild(header);
        header.appendChild(a);
        a.setAttribute("tabindex", "-1");
        a.setAttribute("href", "/");
        a.appendChild(button);
        button.textContent = "Home";

        a = document.createElement("a");
        button = document.createElement("button");
        header.appendChild(a);
        a.setAttribute("tabindex", "-1");
        a.setAttribute("href", "");
        a.appendChild(button);
        button.textContent = "Refresh";

        this.element.appendChild(h1);
        h1.textContent = "Monitor password";

        this.element.appendChild(statusP);
        this.element.appendChild(descriptionP);

        this.element.appendChild(form);
        form.appendChild(formPassword1Input);
        formPassword1Input.setAttribute("type", "password");
        form.onsubmit = this.onFormSubmit.bind(this);
        form.appendChild(document.createElement("br"));
        form.appendChild(formPassword2Input);
        formPassword2Input.setAttribute("type", "password");
        form.appendChild(document.createElement("br"));
        form.appendChild(formChangePasswordButton);
        formChangePasswordButton.setAttribute("type", "submit");
        formChangePasswordButton.textContent = "Change password";

        this.element.appendChild(errorH2);
        errorH2.textContent = "Operational error";
        errorH2.style.display = "none";
        this.element.appendChild(errorPre);
        errorPre.style.display = "none";

        this.loadStatusUi();
        this.loadDescriptionUi();
        this.loadErrorUi();
      }
      MonitorPasswordInterface.prototype.loadStatusJson = function () {
        if (this.status_json_promise) { return; }
        this.status_json_promise = Promise.resolve().then(function () {
          return xhr({url: status_json_url, withCredentials: true, responseType: "json"});
        }).then(function (xhr) {
          return xhr.response;
        });
        this.status_json_promise.catch(function () { return; }).then(function () {
          setTimeout(function () {
            delete this.status_json_promise;
          }.bind(this), 1000);
        }.bind(this));
        return this.status_json_promise;
      };
      MonitorPasswordInterface.prototype.loadStatusUi = function () {
        this.loadStatusJson();
        this.statusP.textContent = "Loading status...";
        return this.status_json_promise.then(function (status_json) {
          if (status_json.status === "OK") {
            this.statusP.innerHTML = "&nbsp;";
          } else {
            this.statusP.textContent = "/!\\ The password needs to be changed at least once! /!\\";
          }
        }.bind(this), function (reason) {
          if (reason && reason.target && reason.target.status === 404) {
            this.statusP.textContent = "/!\\ The password needs to be changed at least once! /!\\";
            return;
          }
          var message = reason && (reason.target && (reason.target.statusText || "Unknown") || reason.message);
          this.statusP.textContent = "Status Json Error: " + (message || "Unknown error");
        }.bind(this)).catch(unexpectedError);
      };
      MonitorPasswordInterface.prototype.loadDescriptionUi = function () {
        this.descriptionP.textContent = [
          "The monitor password is the password used to connect to this interface.",
          "Here you can change the monitor password by filling the formular just below."
        ].join("\n");
      };
      MonitorPasswordInterface.prototype.loadErrorUi = function () {
        this.loadStatusJson();
        this.errorPre.textContent = "Loading error output...";
        return this.status_json_promise.then(function (status_json) {
          if (status_json.error) {
            this.errorH2.style.display = "";
            this.errorPre.style.display = "";
            this.errorPre.textContent = status_json.error;
          } else {
            this.errorH2.style.display = "none";
            this.errorPre.style.display = "none";
            this.errorPre.textContent = "";
          }
        }.bind(this), function (reason) {
          var message = reason && (reason.target && (reason.target.statusText || "Unknown") || reason.message);
          this.errorPre.textContent = "Status Json Error: " + (message || "Unknown error");
        }.bind(this)).catch(unexpectedError);
      };
      MonitorPasswordInterface.prototype.onFormSubmit = function (event) {
        event.preventDefault();
        event.stopPropagation();
        this.execForm();
      };
      MonitorPasswordInterface.prototype.execForm = function () {
        if (this.formPassword1Input.value !== this.formPassword2Input.value) {
          this.statusP.textContent = "The two typed passwords should match!";
          return;
        }
        this.statusP.textContent = "Changing password...";
        var password = this.formPassword1Input.value;
        return Promise.resolve().then(function () {
          return xhr({url: password_cgi_url_part + password, method: "POST", withCredentials: true});
        }).then(function () {
          this.statusP.textContent = "Password changed succesfully!";
          this.formPassword1Input.value = this.formPassword2Input.value = "";
          // rerun promise with new login (also does the relogin)
          xhr({url: rerun_cgi_url, method: "POST", withCredentials: true, username: "admin", password: password});
        }.bind(this), function (reason) {
          var message = reason && (reason.target && (reason.target.statusText || "Unknown") || reason.message);
          this.statusP.textContent = "Status Json Error: " + (message || "Unknown error");
        }.bind(this));
      };
      MonitorPasswordInterface.prototype.runPromiseNow = function () {
        this.runPromiseNowButton.disabled = true;
        var original_text = this.runPromiseNowButton.textContent;
        this.runPromiseNowButton.textContent = "Sending message...";
        return Promise.resolve().then(function () {
          return xhr({url: rerun_cgi_url, method: "POST", withCredentials: true});
        }).catch(unexpectedError).then(function () {
          this.runPromiseNowButton.textContent = original_text;
        }.bind(this));
      };

      /*global setTimeout */
      setTimeout(function () {
        /*global document */
        document.body.innerHTML = "";
        return new MonitorPasswordInterface({rootElement: document.body});
      });

    </script>
  </head>
  <body>
    <h1>Monitor password</h1>
    <noscript>Javascript should be enabled</noscript>
  </body>
</html>