text: "use strict";
  
  var term,
      protocol,
      socketURL,
      socket,
      pid;
  
  var terminalContainer = document.getElementById('terminal-container'),
      actionElements = {
        findNext: document.querySelector('#find-next'),
        findPrevious: document.querySelector('#find-previous')
      },
      optionElements = {
        cursorBlink: document.querySelector('#option-cursor-blink'),
        cursorStyle: document.querySelector('#option-cursor-style'),
        scrollback: document.querySelector('#option-scrollback'),
        tabstopwidth: document.querySelector('#option-tabstopwidth'),
        bellStyle: document.querySelector('#option-bell-style')
      },
      colsElement = document.getElementById('cols'),
      rowsElement = document.getElementById('rows');
  
  function setTerminalSize() {
    var cols = parseInt(colsElement.value, 10);
    var rows = parseInt(rowsElement.value, 10);
    var viewportElement = document.querySelector('.xterm-viewport');
    var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
    var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px';
    var height = (rows * term.charMeasure.height).toString() + 'px';
  
    terminalContainer.style.width = width;
    terminalContainer.style.height = height;
    term.resize(cols, rows);
  }
  
  colsElement.addEventListener('change', setTerminalSize);
  rowsElement.addEventListener('change', setTerminalSize);
  
  actionElements.findNext.addEventListener('keypress', function (e) {
    if (e.key === "Enter") {
      e.preventDefault();
      term.findNext(actionElements.findNext.value);
    }
  });
  actionElements.findPrevious.addEventListener('keypress', function (e) {
    if (e.key === "Enter") {
      e.preventDefault();
      term.findPrevious(actionElements.findPrevious.value);
    }
  });
  
  optionElements.cursorBlink.addEventListener('change', function () {
    term.setOption('cursorBlink', optionElements.cursorBlink.checked);
  });
  optionElements.cursorStyle.addEventListener('change', function () {
    term.setOption('cursorStyle', optionElements.cursorStyle.value);
  });
  optionElements.bellStyle.addEventListener('change', function () {
    term.setOption('bellStyle', optionElements.bellStyle.value);
  });
  optionElements.scrollback.addEventListener('change', function () {
    term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
  });
  optionElements.tabstopwidth.addEventListener('change', function () {
    term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
  });
  
  createTerminal();
  
  function createTerminal() {
    // Clean terminal
    while (terminalContainer.children.length) {
      terminalContainer.removeChild(terminalContainer.children[0]);
    }
    term = new Terminal({
      cursorBlink: optionElements.cursorBlink.checked,
      scrollback: parseInt(optionElements.scrollback.value, 10),
      tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
    });
    term.on('resize', function (size) {
      if (!pid) {
        return;
      }
      var cols = size.cols,
          rows = size.rows,
          url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
  
      fetch(url, {method: 'POST'});
    });
    protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
    socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
  
    term.open(terminalContainer);
    term.fit();
  
    // fit is called within a setTimeout, cols and rows need this.
    setTimeout(function () {
      colsElement.value = term.cols;
      rowsElement.value = term.rows;
  
      // Set terminal size again to set the specific dimensions on the demo
      setTerminalSize();
  
      fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
  
        res.text().then(function (pid) {
          window.pid = pid;
          socketURL += pid;
          socket = new WebSocket(socketURL);
          socket.onopen = runRealTerminal;
          socket.onclose = runFakeTerminal;
          socket.onerror = runFakeTerminal;
  
          term.zmodemAttach(socket, {
              noTerminalWriteOutsideSession: true,
          } );
  
          term.on("zmodemRetract", () => {
              start_form.style.display = "none";
              start_form.onsubmit = null;
          });
  
          term.on("zmodemDetect", (detection) => {
              function do_zmodem() {
                  term.detach();
                  let zsession = detection.confirm();
  
                  var promise;
  
                  if (zsession.type === "receive") {
                      promise = _handle_receive_session(zsession);
                  }
                  else {
                      promise = _handle_send_session(zsession);
                  }
  
                  promise.catch( console.error.bind(console) ).then( () => {
                      term.attach(socket);
                  } );
              }
  
              if (_auto_zmodem()) {
                  do_zmodem();
              }
              else {
                  start_form.style.display = "";
                  start_form.onsubmit = function(e) {
                      start_form.style.display = "none";
  
                      if (document.getElementById("zmstart_yes").checked) {
                          do_zmodem();
                      }
                      else {
                          detection.deny();
                      }
                  };
              }
          });
        });
      });
    }, 0);
  }
  
  //----------------------------------------------------------------------
  // UI STUFF
  
  function _show_file_info(xfer) {
      var file_info = xfer.get_details();
  
      document.getElementById("name").textContent = file_info.name;
      document.getElementById("size").textContent = file_info.size;
      document.getElementById("mtime").textContent = file_info.mtime;
      document.getElementById("files_remaining").textContent = file_info.files_remaining;
      document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining;
  
      document.getElementById("mode").textContent = "0" + file_info.mode.toString(8);
  
      var xfer_opts = xfer.get_options();
      ["conversion", "management", "transport", "sparse"].forEach( (lbl) => {
          document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl];
      } );
  
      document.getElementById("zm_file").style.display = "";
  }
  function _hide_file_info() {
      document.getElementById("zm_file").style.display = "none";
  }
  
  function _save_to_disk(xfer, buffer) {
      return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name);
  }
  
  var skipper_button = document.getElementById("zm_progress_skipper");
  var skipper_button_orig_text = skipper_button.textContent;
  
  function _show_progress() {
      skipper_button.disabled = false;
      skipper_button.textContent = skipper_button_orig_text;
  
      document.getElementById("bytes_received").textContent = 0;
      document.getElementById("percent_received").textContent = 0;
  
      document.getElementById("zm_progress").style.display = "";
  }
  
  function _update_progress(xfer) {
      var total_in = xfer.get_offset();
  
      document.getElementById("bytes_received").textContent = total_in;
  
      var percent_received = 100 * total_in / xfer.get_details().size;
      document.getElementById("percent_received").textContent = percent_received.toFixed(2);
  }
  
  function _hide_progress() {
      document.getElementById("zm_progress").style.display = "none";
  }
  
  var start_form = document.getElementById("zm_start");
  
  function _auto_zmodem() {
      return document.getElementById("zmodem-auto").checked;
  }
  
  // END UI STUFF
  //----------------------------------------------------------------------
  
  function _handle_receive_session(zsession) {
      zsession.on("offer", function(xfer) {
          current_receive_xfer = xfer;
  
          _show_file_info(xfer);
  
          var offer_form = document.getElementById("zm_offer");
  
          function on_form_submit() {
              offer_form.style.display = "none";
  
              //START
              //if (offer_form.zmaccept.value) {
              if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) {
                  _show_progress();
  
                  var FILE_BUFFER = [];
                  xfer.on("input", (payload) => {
                      _update_progress(xfer);
                      FILE_BUFFER.push( new Uint8Array(payload) );
                  });
                  xfer.accept().then(
                      () => {
                          _save_to_disk(xfer, FILE_BUFFER);
                      },
                      console.error.bind(console)
                  );
              }
              else {
                  xfer.skip();
              }
              //END
          }
  
          if (_auto_zmodem()) {
              on_form_submit();
          }
          else {
              offer_form.onsubmit = on_form_submit;
              offer_form.style.display = "";
          }
      } );
  
      var promise = new Promise( (res) => {
          zsession.on("session_end", () => {
              _hide_file_info();
              _hide_progress();
              res();
          } );
      } );
  
      zsession.start();
  
      return promise;
  }
  
  function _handle_send_session(zsession) {
      var choose_form = document.getElementById("zm_choose");
      choose_form.style.display = "";
  
      var file_el = document.getElementById("zm_files");
  
      var promise = new Promise( (res) => {
          file_el.onchange = function(e) {
              choose_form.style.display = "none";
  
              var files_obj = file_el.files;
  
              Zmodem.Browser.send_files(
                  zsession,
                  files_obj,
                  {
                      on_offer_response(obj, xfer) {
                          if (xfer) _show_progress();
                          //console.log("offer", xfer ? "accepted" : "skipped");
                      },
                      on_progress(obj, xfer) {
                          _update_progress(xfer);
                      },
                      on_file_complete(obj) {
                          //console.log("COMPLETE", obj);
                          _hide_progress();
                      },
                  }
              ).then(_hide_progress).then(
                  zsession.close.bind(zsession),
                  console.error.bind(console)
              ).then( () => {
                  _hide_file_info();
                  _hide_progress();
                  res();
              } );
          };
      } );
  
      return promise;
  }
  
  //This is here to allow canceling of an in-progress ZMODEM transfer.
  var current_receive_xfer;
  
  //Called from HTML directly.
  function skip_current_file() {
      current_receive_xfer.skip();
  
      skipper_button.disabled = true;
      skipper_button.textContent = "Waiting for server to acknowledge skip …";
  }
  
  function runRealTerminal() {
      term.attach(socket);
  
      term._initialized = true;
  }
  
  function runFakeTerminal() {
    if (term._initialized) {
      return;
    }
  
    term._initialized = true;
  
    var shellprompt = '$ ';
  
    term.prompt = function () {
      term.write('\r\n' + shellprompt);
    };
  
    term.writeln('Welcome to xterm.js');
    term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
    term.writeln('Type some keys and commands to play around.');
    term.writeln('');
    term.prompt();
  
    term.on('key', function (key, ev) {
      var printable = (
        !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
      );
  
      if (ev.keyCode == 13) {
        term.prompt();
      } else if (ev.keyCode == 8) {
       // Do not delete the prompt
        if (term.x > 2) {
          term.write('\b \b');
        }
      } else if (printable) {
        term.write(key);
      }
    });
  
    term.on('paste', function (data, ev) {
      term.write(data);
    });
  }
  