<commit_before><% if @console_session && @console_session.binding %>
  <div id="console"
    data-remote-path='<%= "console/repl_sessions/#{@console_session.id}" %>'
    data-initial-prompt='<%= @console_session.prompt %>'>
  </div>

  <script type="text/javascript">
    (function() {
      // DOM helpers
      function hasClass(el, className) {
        var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
        return el.className.match(regex);
      }

      function addClass(el, className) {
        el.className += " " + className;
      }

      function removeClass(el, className) {
        var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
        el.className = el.className.replace(regex, '');
      }

      function removeAllChildren(el) {
        while (el.firstChild) {
          el.removeChild(el.firstChild);
        }
      }

      function escapeHTML(html) {
        return html
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
          .replace(/"/g, '&quot;')
          .replace(/'/g, '&#x27;')
          .replace(/`/g, '&#x60;');
      }

      // Add CSS styles dynamically. This probably doesnt work for IE <8.
      var style = document.createElement('style');
      style.type = 'text/css';
      style.innerHTML =
        "#console { position: fixed; left: 0; bottom: 0; width: 100%; height: 150px; border: 1px solid; padding-bottom: 20px; } " +
        "#console div.console-inner { font-family: monospace; font-size: 11px; height: 100%; overflow: auto; background: #333; padding: 10px; } " +
        "#console div.console-prompt-box { color: #FFF; } " +
        "#console pre.console-message { color: #1AD027; margin: 0; } " +
        "#console div.console-focus span.console-cursor { background: #FEFEFE; color: #333; font-weight: bold; }";
      document.getElementsByTagName('head')[0].appendChild(style);

      /**
       * Constructor for command storage.
       * It uses localStorage if available. Otherwise fallback to normal JS array.
       */
      function CommandStorage() {
        this.previousCommands = [];
        var previousCommandOffset = 0;
        var hasLocalStorage = typeof window.localStorage !== 'undefined';
        var STORAGE_KEY = "web_console_previous_commands";
        var MAX_STORAGE = 100;

        if (hasLocalStorage) {
          this.previousCommands = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
          previousCommandOffset = this.previousCommands.length;
        }

        this.addCommand = function(command) {
          previousCommandOffset = this.previousCommands.push(command);

          if (previousCommandOffset > MAX_STORAGE) {
            this.previousCommands.splice(0, 1);
            previousCommandOffset = MAX_STORAGE;
          }

          if (hasLocalStorage) {
            localStorage.setItem(STORAGE_KEY, JSON.stringify(this.previousCommands));
          }
        };

        this.navigate = function(offset) {
          previousCommandOffset += offset;

          if (previousCommandOffset < 0) {
            previousCommandOffset = -1;
            return null;
          }

          if (previousCommandOffset >= this.previousCommands.length) {
            previousCommandOffset = this.previousCommands.length;
            return null;
          }

          return this.previousCommands[previousCommandOffset];
        }
      }

      // HTML strings for dynamic elements.
      var consoleInnerHtml = [
        "<div id='resizer' style='width:100%; height: 3px; cursor: ns-resize;'></div>",
        "<div class='console-inner'></div>",
        "<input id='clipboard' type='text'>"
      ].join("");

      var promptBoxHtml = [
        "<span class='console-prompt-label' style='display: inline;'></span>",
        "<pre class='console-prompt-display' style='display: inline;'></pre>"
      ].join("");

      // REPLConsole Constructor
      function REPLConsole(config) {
        this.commandStorage = new CommandStorage();
        this.prompt = config && config.promptLabel ? config.promptLabel : ' >>';
        this.commandHandle = config && config.commandHandle ? config.commandHandle : function() { return this; }
      }


      REPLConsole.prototype.install = function(container) {
        var _this = this;

        document.onkeydown = function(ev) {
          if (_this.focused) { _this.onKeyDown(ev); }
        };

        document.onkeypress = function(ev) {
          if (_this.focused) { _this.onKeyPress(ev); }
        };

        document.addEventListener('mousedown', function(ev) {
          var el = ev.target || ev.srcElement;

          if (el) {
            do {
              if (el === container) {
                _this.focus();
                return;
              }
            } while (el = el.parentNode);

            _this.blur();
          }
        });

        // Render the console.
        container.innerHTML = consoleInnerHtml;
        // Make the console resizable.
        document.getElementById('resizer').addEventListener('mousedown', function(ev) {
          var startY = ev.clientY;
          var startHeight = parseInt(document.defaultView.getComputedStyle(consoleDiv).height, 10);

          var doDrag = function(e) {
            consoleDiv.style.height = (startHeight + startY - e.clientY) + 'px';
          };

          var stopDrag = function(e) {
            document.documentElement.removeEventListener('mousemove', doDrag, false);
            document.documentElement.removeEventListener('mouseup', stopDrag, false);
          };

          document.documentElement.addEventListener('mousemove', doDrag, false);
          document.documentElement.addEventListener('mouseup', stopDrag, false);
        });

        // Initialize
        this.inner = container.getElementsByClassName('console-inner')[0];
        this.clipboard = document.getElementById('clipboard');
        this.newPromptBox();
        this.focus();
      };

      REPLConsole.prototype.focus = function() {
        this.focused = true;
        if (! hasClass(this.inner, "console-focus")) {
          addClass(this.inner, "console-focus");
        }
        this.scrollToBottom();
      };

      REPLConsole.prototype.blur = function() {
        this.focused = false;
        removeClass(this.inner, "console-focus");
      };

      /**
       * Add a new empty prompt box to the console.
       */
      REPLConsole.prototype.newPromptBox = function() {
        // Remove the caret from previous prompt display if any.
        if (this.promptDisplay) {
          this.removeCaretFromPrompt();
        }

        var promptBox = document.createElement('div');
        promptBox.className = "console-prompt-box";
        promptBox.innerHTML = promptBoxHtml;
        this.promptLabel = promptBox.getElementsByClassName('console-prompt-label')[0];
        this.promptDisplay = promptBox.getElementsByClassName('console-prompt-display')[0];
        // Render the prompt box
        this.setInput("");
        this.promptLabel.innerHTML = this.prompt;
        this.inner.appendChild(promptBox);
        this.scrollToBottom();
      };

      /**
       * Remove the caret from the prompt box,
       * mainly before adding a new prompt box.
       * For simplicity, just re-render the prompt box
       * with caret position -1.
       */
      REPLConsole.prototype.removeCaretFromPrompt = function() {
        this.setInput(this._input, -1);
      };

      REPLConsole.prototype.setInput = function(input, caretPos) {
        this._caretPos = caretPos === undefined ? input.length : caretPos;
        this._input = input;
        this.renderInput();
      };

      /**
       * Add some text to the existing input.
       */
      REPLConsole.prototype.addToInput = function(val, caretPos) {
        caretPos = caretPos || this._caretPos;
        var before = this._input.substring(0, caretPos);
        var after = this._input.substring(caretPos, this._input.length);
        var newInput =  before + val + after;
        this.setInput(newInput, caretPos + val.length);
      };

      /**
       * Render the input prompt. This is called whenever
       * the user input changes, sometimes not very efficient.
       */
      REPLConsole.prototype.renderInput = function() {
        // Clear the current input.
        removeAllChildren(this.promptDisplay);

        var promptCursor = document.createElement('span');
        promptCursor.className = "console-cursor";
        var before, current, after;

        if (this._caretPos < 0) {
          before = this._input;
          current = after = "";
        } else if (this._caretPos === this._input.length) {
          before = this._input;
          current = "\u00A0";
          after = "";
        } else {
          before = this._input.substring(0, this._caretPos);
          current = this._input.charAt(this._caretPos);
          after = this._input.substring(this._caretPos + 1, this._input.length);
        }

        this.promptDisplay.appendChild(document.createTextNode(before));
        promptCursor.appendChild(document.createTextNode(current));
        this.promptDisplay.appendChild(promptCursor);
        this.promptDisplay.appendChild(document.createTextNode(after));
      };

      REPLConsole.prototype.writeOutput = function(output) {
        var consoleMessage = document.createElement('pre');
        consoleMessage.className = "console-message";
        consoleMessage.innerHTML = escapeHTML(output);
        this.inner.appendChild(consoleMessage);
        this.newPromptBox();
      };

      REPLConsole.prototype.onEnterKey = function() {
        var input = this._input;

        if(input != "" && input !== undefined) {
          this.commandStorage.addCommand(input);
        }

        this.commandHandle(input);
      };

      REPLConsole.prototype.onNavigateHistory = function(offset) {
        var command = this.commandStorage.navigate(offset) || "";
        this.setInput(command);
      };

      /**
       * Handle control keys like up, down, left, right.
       */
      REPLConsole.prototype.onKeyDown = function(ev) {
        switch (ev.keyCode) {
          case 13:
            // Enter key
            this.onEnterKey();
            ev.preventDefault();
            break;
          case 80:
            // Ctrl-P
            if (! ev.ctrlKey) break;
          case 38:
            // Up arrow
            this.onNavigateHistory(-1);
            ev.preventDefault();
            break;
          case 78:
            // Ctrl-N
            if (! ev.ctrlKey) break;
          case 40:
            // Down arrow
            this.onNavigateHistory(1);
            ev.preventDefault();
            break;
          case 37:
            // Left arrow
            var caretPos = this._caretPos > 0 ? this._caretPos - 1 : this._caretPos;
            this.setInput(this._input, caretPos);
            ev.preventDefault();
            break;
          case 39:
            // Right arrow
            var length = this._input.length;
            var caretPos = this._caretPos < length ? this._caretPos + 1 : this._caretPos;
            this.setInput(this._input, caretPos);
            ev.preventDefault();
            break;
          case 8:
            // Delete
            this.deleteAtCurrent();
            ev.preventDefault();
            break;
          case 86:
            // ctrl/command-v
            if (ev.ctrlKey || ev.metaKey) {
              // Let the pasted text go to our clipboard.
              this.clipboard.focus();

              // Pasting to clipboard doesn't happen immediately,
              // so we have to wait for a while to get the pasted text.
              var _this = this;
              setTimeout(function() {
                _this.addToInput(_this.clipboard.value);
                _this.clipboard.value = "";
                _this.clipboard.blur();
              }, 10);
            }
            break;
          default:
            break;
        }

        ev.stopPropagation();
      };

      /**
       * Handle input key press.
       */
      REPLConsole.prototype.onKeyPress = function(ev) {
        // Only write to the console if it's a single key press.
        if (ev.ctrlKey || ev.metaKey) { return; }
        var keyCode = ev.keyCode || ev.which;
        this.insertAtCurrent(String.fromCharCode(keyCode));
        ev.stopPropagation();
        ev.preventDefault();
      };

      /**
       * Delete a character at the current position.
       */
      REPLConsole.prototype.deleteAtCurrent = function() {
        if (this._caretPos > 0) {
          var caretPos = this._caretPos - 1;
          var before = this._input.substring(0, caretPos);
          var after = this._input.substring(this._caretPos, this._input.length);
          this.setInput(before + after, caretPos);
        }
      };

      /**
       * Insert a character at the current position.
       */
      REPLConsole.prototype.insertAtCurrent = function(char) {
        var before = this._input.substring(0, this._caretPos);
        var after = this._input.substring(this._caretPos, this._input.length);
        this.setInput(before + char + after, this._caretPos + 1);
      };

      REPLConsole.prototype.scrollToBottom = function() {
        this.inner.scrollTop = this.inner.scrollHeight;
      };

      window.REPLConsole = REPLConsole;
    })();

    // Install the console
    var consoleDiv = document.getElementById('console');
    var replConsole = new REPLConsole({
      promptLabel: consoleDiv.dataset.initialPrompt,
      commandHandle: function(line) {
        var _this = this;
        var xhr = new XMLHttpRequest();
        var url = consoleDiv.dataset.remotePath;
        var params = "input=" + encodeURIComponent(line);

        xhr.open("PUT", url, true);
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        xhr.send(params);

        xhr.onreadystatechange = function() {
          if(xhr.readyState == 4 && xhr.status == 200) {
            var response = JSON.parse(xhr.responseText);
            _this.writeOutput(response.output);
          }
        }
      }
    });

    replConsole.install(consoleDiv);
  </script>
<% end %>
<commit_msg>Fix web-console appearance in bootstrap3 projects<commit_after><% if @console_session && @console_session.binding %>
  <div id="console"
    data-remote-path='<%= "console/repl_sessions/#{@console_session.id}" %>'
    data-initial-prompt='<%= @console_session.prompt %>'>
  </div>

  <script type="text/javascript">
    (function() {
      // DOM helpers
      function hasClass(el, className) {
        var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
        return el.className.match(regex);
      }

      function addClass(el, className) {
        el.className += " " + className;
      }

      function removeClass(el, className) {
        var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
        el.className = el.className.replace(regex, '');
      }

      function removeAllChildren(el) {
        while (el.firstChild) {
          el.removeChild(el.firstChild);
        }
      }

      function escapeHTML(html) {
        return html
          .replace(/&/g, '&amp;')
          .replace(/</g, '&lt;')
          .replace(/>/g, '&gt;')
          .replace(/"/g, '&quot;')
          .replace(/'/g, '&#x27;')
          .replace(/`/g, '&#x60;');
      }

      // Add CSS styles dynamically. This probably doesnt work for IE <8.
      var style = document.createElement('style');
      style.type = 'text/css';
      style.innerHTML =
        "#console { position: fixed; left: 0; bottom: 0; width: 100%; height: 150px; border: 1px solid; padding: 0 0 20px 0; color: #1AD027; overflow: none; margin: 0; background: none repeat scroll 0% 0% #333; } " +
        "#console div.console-inner { font-family: monospace; font-size: 11px; height: 100%; overflow: auto; background: #333; border: 0; padding:0 margin: 0 } " +
        "#console div.console-prompt-box { color: #fff; } " +
        "#console pre.console-message { color: #1AD027; margin: 0; border: 0; white-space: pre-wrap; background-color: #333; padding: 0} " +
        "#console div.console-focus span.console-cursor { background: #FEFEFE; color: #333; font-weight: bold; }";
      document.getElementsByTagName('head')[0].appendChild(style);

      /**
       * Constructor for command storage.
       * It uses localStorage if available. Otherwise fallback to normal JS array.
       */
      function CommandStorage() {
        this.previousCommands = [];
        var previousCommandOffset = 0;
        var hasLocalStorage = typeof window.localStorage !== 'undefined';
        var STORAGE_KEY = "web_console_previous_commands";
        var MAX_STORAGE = 100;

        if (hasLocalStorage) {
          this.previousCommands = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
          previousCommandOffset = this.previousCommands.length;
        }

        this.addCommand = function(command) {
          previousCommandOffset = this.previousCommands.push(command);

          if (previousCommandOffset > MAX_STORAGE) {
            this.previousCommands.splice(0, 1);
            previousCommandOffset = MAX_STORAGE;
          }

          if (hasLocalStorage) {
            localStorage.setItem(STORAGE_KEY, JSON.stringify(this.previousCommands));
          }
        };

        this.navigate = function(offset) {
          previousCommandOffset += offset;

          if (previousCommandOffset < 0) {
            previousCommandOffset = -1;
            return null;
          }

          if (previousCommandOffset >= this.previousCommands.length) {
            previousCommandOffset = this.previousCommands.length;
            return null;
          }

          return this.previousCommands[previousCommandOffset];
        }
      }

      // HTML strings for dynamic elements.
      var consoleInnerHtml = [
        "<div id='resizer' style='width:100%; height: 3px; cursor: ns-resize;'></div>",
        "<div class='console-inner'></div>",
        "<input id='clipboard' type='text'>"
      ].join("");

      var promptBoxHtml = [
        "<span class='console-prompt-label' style='display: inline; color: #fff; background: none repeat scroll 0% 0% #333; border: 0; padding: 0'></span>",
        "<pre class='console-prompt-display' style='display: inline; color: #fff; background: none repeat scroll 0% 0% #333; border: 0; padding: 0'></pre>"
      ].join("");

      // REPLConsole Constructor
      function REPLConsole(config) {
        this.commandStorage = new CommandStorage();
        this.prompt = config && config.promptLabel ? config.promptLabel : ' >>';
        this.commandHandle = config && config.commandHandle ? config.commandHandle : function() { return this; }
      }


      REPLConsole.prototype.install = function(container) {
        var _this = this;

        document.onkeydown = function(ev) {
          if (_this.focused) { _this.onKeyDown(ev); }
        };

        document.onkeypress = function(ev) {
          if (_this.focused) { _this.onKeyPress(ev); }
        };

        document.addEventListener('mousedown', function(ev) {
          var el = ev.target || ev.srcElement;

          if (el) {
            do {
              if (el === container) {
                _this.focus();
                return;
              }
            } while (el = el.parentNode);

            _this.blur();
          }
        });

        // Render the console.
        container.innerHTML = consoleInnerHtml;
        // Make the console resizable.
        document.getElementById('resizer').addEventListener('mousedown', function(ev) {
          var startY = ev.clientY;
          var startHeight = parseInt(document.defaultView.getComputedStyle(consoleDiv).height, 10);

          var doDrag = function(e) {
            consoleDiv.style.height = (startHeight + startY - e.clientY) + 'px';
          };

          var stopDrag = function(e) {
            document.documentElement.removeEventListener('mousemove', doDrag, false);
            document.documentElement.removeEventListener('mouseup', stopDrag, false);
          };

          document.documentElement.addEventListener('mousemove', doDrag, false);
          document.documentElement.addEventListener('mouseup', stopDrag, false);
        });

        // Initialize
        this.inner = container.getElementsByClassName('console-inner')[0];
        this.clipboard = document.getElementById('clipboard');
        this.newPromptBox();
        this.focus();
      };

      REPLConsole.prototype.focus = function() {
        this.focused = true;
        if (! hasClass(this.inner, "console-focus")) {
          addClass(this.inner, "console-focus");
        }
        this.scrollToBottom();
      };

      REPLConsole.prototype.blur = function() {
        this.focused = false;
        removeClass(this.inner, "console-focus");
      };

      /**
       * Add a new empty prompt box to the console.
       */
      REPLConsole.prototype.newPromptBox = function() {
        // Remove the caret from previous prompt display if any.
        if (this.promptDisplay) {
          this.removeCaretFromPrompt();
        }

        var promptBox = document.createElement('div');
        promptBox.className = "console-prompt-box";
        promptBox.innerHTML = promptBoxHtml;
        this.promptLabel = promptBox.getElementsByClassName('console-prompt-label')[0];
        this.promptDisplay = promptBox.getElementsByClassName('console-prompt-display')[0];
        // Render the prompt box
        this.setInput("");
        this.promptLabel.innerHTML = this.prompt;
        this.inner.appendChild(promptBox);
        this.scrollToBottom();
      };

      /**
       * Remove the caret from the prompt box,
       * mainly before adding a new prompt box.
       * For simplicity, just re-render the prompt box
       * with caret position -1.
       */
      REPLConsole.prototype.removeCaretFromPrompt = function() {
        this.setInput(this._input, -1);
      };

      REPLConsole.prototype.setInput = function(input, caretPos) {
        this._caretPos = caretPos === undefined ? input.length : caretPos;
        this._input = input;
        this.renderInput();
      };

      /**
       * Add some text to the existing input.
       */
      REPLConsole.prototype.addToInput = function(val, caretPos) {
        caretPos = caretPos || this._caretPos;
        var before = this._input.substring(0, caretPos);
        var after = this._input.substring(caretPos, this._input.length);
        var newInput =  before + val + after;
        this.setInput(newInput, caretPos + val.length);
      };

      /**
       * Render the input prompt. This is called whenever
       * the user input changes, sometimes not very efficient.
       */
      REPLConsole.prototype.renderInput = function() {
        // Clear the current input.
        removeAllChildren(this.promptDisplay);

        var promptCursor = document.createElement('span');
        promptCursor.className = "console-cursor";
        var before, current, after;

        if (this._caretPos < 0) {
          before = this._input;
          current = after = "";
        } else if (this._caretPos === this._input.length) {
          before = this._input;
          current = "\u00A0";
          after = "";
        } else {
          before = this._input.substring(0, this._caretPos);
          current = this._input.charAt(this._caretPos);
          after = this._input.substring(this._caretPos + 1, this._input.length);
        }

        this.promptDisplay.appendChild(document.createTextNode(before));
        promptCursor.appendChild(document.createTextNode(current));
        this.promptDisplay.appendChild(promptCursor);
        this.promptDisplay.appendChild(document.createTextNode(after));
      };

      REPLConsole.prototype.writeOutput = function(output) {
        var consoleMessage = document.createElement('pre');
        consoleMessage.className = "console-message";
        consoleMessage.innerHTML = escapeHTML(output);
        this.inner.appendChild(consoleMessage);
        this.newPromptBox();
      };

      REPLConsole.prototype.onEnterKey = function() {
        var input = this._input;

        if(input != "" && input !== undefined) {
          this.commandStorage.addCommand(input);
        }

        this.commandHandle(input);
      };

      REPLConsole.prototype.onNavigateHistory = function(offset) {
        var command = this.commandStorage.navigate(offset) || "";
        this.setInput(command);
      };

      /**
       * Handle control keys like up, down, left, right.
       */
      REPLConsole.prototype.onKeyDown = function(ev) {
        switch (ev.keyCode) {
          case 13:
            // Enter key
            this.onEnterKey();
            ev.preventDefault();
            break;
          case 80:
            // Ctrl-P
            if (! ev.ctrlKey) break;
          case 38:
            // Up arrow
            this.onNavigateHistory(-1);
            ev.preventDefault();
            break;
          case 78:
            // Ctrl-N
            if (! ev.ctrlKey) break;
          case 40:
            // Down arrow
            this.onNavigateHistory(1);
            ev.preventDefault();
            break;
          case 37:
            // Left arrow
            var caretPos = this._caretPos > 0 ? this._caretPos - 1 : this._caretPos;
            this.setInput(this._input, caretPos);
            ev.preventDefault();
            break;
          case 39:
            // Right arrow
            var length = this._input.length;
            var caretPos = this._caretPos < length ? this._caretPos + 1 : this._caretPos;
            this.setInput(this._input, caretPos);
            ev.preventDefault();
            break;
          case 8:
            // Delete
            this.deleteAtCurrent();
            ev.preventDefault();
            break;
          case 86:
            // ctrl/command-v
            if (ev.ctrlKey || ev.metaKey) {
              // Let the pasted text go to our clipboard.
              this.clipboard.focus();

              // Pasting to clipboard doesn't happen immediately,
              // so we have to wait for a while to get the pasted text.
              var _this = this;
              setTimeout(function() {
                _this.addToInput(_this.clipboard.value);
                _this.clipboard.value = "";
                _this.clipboard.blur();
              }, 10);
            }
            break;
          default:
            break;
        }

        ev.stopPropagation();
      };

      /**
       * Handle input key press.
       */
      REPLConsole.prototype.onKeyPress = function(ev) {
        // Only write to the console if it's a single key press.
        if (ev.ctrlKey || ev.metaKey) { return; }
        var keyCode = ev.keyCode || ev.which;
        this.insertAtCurrent(String.fromCharCode(keyCode));
        ev.stopPropagation();
        ev.preventDefault();
      };

      /**
       * Delete a character at the current position.
       */
      REPLConsole.prototype.deleteAtCurrent = function() {
        if (this._caretPos > 0) {
          var caretPos = this._caretPos - 1;
          var before = this._input.substring(0, caretPos);
          var after = this._input.substring(this._caretPos, this._input.length);
          this.setInput(before + after, caretPos);
        }
      };

      /**
       * Insert a character at the current position.
       */
      REPLConsole.prototype.insertAtCurrent = function(char) {
        var before = this._input.substring(0, this._caretPos);
        var after = this._input.substring(this._caretPos, this._input.length);
        this.setInput(before + char + after, this._caretPos + 1);
      };

      REPLConsole.prototype.scrollToBottom = function() {
        this.inner.scrollTop = this.inner.scrollHeight;
      };

      window.REPLConsole = REPLConsole;
    })();

    // Install the console
    var consoleDiv = document.getElementById('console');
    var replConsole = new REPLConsole({
      promptLabel: consoleDiv.dataset.initialPrompt,
      commandHandle: function(line) {
        var _this = this;
        var xhr = new XMLHttpRequest();
        var url = consoleDiv.dataset.remotePath;
        var params = "input=" + encodeURIComponent(line);

        xhr.open("PUT", url, true);
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        xhr.send(params);

        xhr.onreadystatechange = function() {
          if(xhr.readyState == 4 && xhr.status == 200) {
            var response = JSON.parse(xhr.responseText);
            _this.writeOutput(response.output);
          }
        }
      }
    });

    replConsole.install(consoleDiv);
  </script>
<% end %>

{'fraction_non_alphanumeric': 0.1252973010237496, 'fraction_numerical': 0.007652269828685671, 'mean_word_length': 2.0211392273247943, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7587667', 'n_tokens_mistral': 8212, 'n_tokens_neox': 7871, 'n_words': 1688}