如何评估用户在web应用程序中输入的代码

How can I evaluate a users code input in my web application

本文关键字:应用程序 web 输入 代码 用户 何评估 评估      更新时间:2023-09-26

我正在尝试构建一个演示站点来帮助其他人学习代码。我在评估来自代码的用户输入时遇到问题。我知道我可以使用eval()函数,但我会有很多例子,拥有许多eval()函数似乎不是很理想或简洁。如果我使用eval(),实现最佳实践的最佳方式是什么?

或者,有没有一种方法可以实现一个简单的编译器或通用函数,并指定一个按钮点击来运行所述函数,该函数将在编辑器中评估代码并将其显示在页面上的某个位置。

我目前正在使用Ace作为我的编辑器。也许他们的api中有一个函数可以做到这一点,但我找不到。我也知道我可以使用代码镜像,但也不确定他们的解决方案。如有任何帮助,我们将不胜感激。

这里有一个指向JSBIN的链接(注意:代码编辑器没有显示在这里,因为我不需要库

这是一个链接到我有功能版本的网站。

如果你需要看到代码,我已经发布在下面:

HTML

    <!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>Learn to Code with Codesmith</title>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
  <link href='http://fonts.googleapis.com/css?family=Open+Sans:400;300' rel='stylesheet' type='text/css'>
  <link href='style.css' rel='stylesheet'>
  <style type="text/css" media="screen">
  </style>
</head>
<body>
  <!-- <div class="page-wrapper">
    <a class="btn trigger" href="javascript:;">Click Me!</a>
  </div> -->
  <div class="modal-wrapper">
    <div class="modal">
      <div class="head">
        <a class="btn-close trigger" href="javascript:;"></a>
      </div>
      <div class="content">
        <form class="" action="" method="post">
          <input id="username" type="text" name="name" value="" placeholder="Username">
          <input id="password" type="text" name="name" value="" placeholder="Password">
          <button id="login-submit" type="button" name="button">LOGIN</button>
        </form>
      </div>
    </div>
  </div>
  <div class="menu">
    <!-- Menu icon -->
    <div class="icon-close">
      <img src="http://s3.amazonaws.com/codecademy-content/courses/ltp2/img/uber/close.png">
    </div>
    <!-- Menu -->
    <ul>
      <li class="main-cat"><a href="#">FOUNDATIONS</a></li>
      <ul>
        <li class="sub-cat"><a href="#">Intro to Javascript</a></li>
        <li class="sub-cat"><a href="#">Algorithms</a></li>
        <li class="sub-cat"><a href="#">Data Structures</a></li>
        <li class="sub-cat"><a href="#">Data Types</a></li>
        <li class="sub-cat"><a href="#">Syntax</a></li>
        <li class="sub-cat"><a href="#">Variables</a></li>
        <li class="sub-cat"><a href="#">Strings</a></li>
        <li class="sub-cat"><a href="#">Arrays</a></li>
        <li class="sub-cat"><a href="#">Objects</a></li>
        <li class="sub-cat"><a href="#">Functions</a></li>
        <li class="sub-cat"><a href="#">Scope</a></li>
      </ul>
      <li class="main-cat"><a href="#">Intermediate</a></li>
      <li class="main-cat"><a href="#">Advanced</a></li>
    </ul>
  </div>

  <!-- Main body -->
  <div class="nav">
    <button class="sign-up" type="button" name="button" onclick="">SIGN UP</button>
    <!-- <a class="btn trigger" href="javascript:;">Click Me!</a> -->
    <!-- <button class="login trigger" type="button" name="button" onclick="">LOGIN</button> -->
    <a class="login trigger" type="button" name="button" onclick="" href="javascript:;">LOGIN</a>
    <div class="icon-menu">
      <i class="fa fa-bars"></i>
      Menu
    </div>
  <div class="video">
    <iframe width="560" height="315" src="https://www.youtube.com/embed/JeyH_8pWVJ4" frameborder="0" allowfullscreen></iframe>
  </div>
  <div class="code-snippet sandbox">
    <pre id="editor"> // Write your code here and when finished, click the run button below...
    <!-- function greeting() {
      console.log("Hello, World!");
    } -->
    </pre>

    <div id="run-code">
      <button class="run-code" type="button" name="button" onclick="evaluate()" >RUN</button>
    </div>
  </div>
  <!-- <textarea class="code-eval" name="output" rows="8" cols="40"></textarea> -->

  <!-- <a class="jsbin-embed" href="http://jsbin.com/iwovaj/73/embed?html,output&height=315px&width=700px"></a>
  <script src="http://static.jsbin.com/js/embed.js"></script> -->
  <script src="src-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
  <script>
      var editor = ace.edit("editor");
      editor.setTheme("ace/theme/chaos");
      editor.session.setMode("ace/mode/javascript");
      editor.session.getLength(true);
      document.getElementById('editor').style.fontSize='14px';
      editor.getSession().setUseWrapMode(true);
      editor.setHighlightActiveLine(true);
  </script>
  <script src="sandbox.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script></script>
  <script src="app.js"></script>
</body>
</html>

CSS

html, body{
  width:100%;
  height:100%;
  margin:0;
}
body {
    overflow: hidden;
    left: 0;
    margin: 0;
    overflow: hidden;
    position: relative;
}
#editor {
    margin: 0;
    position: relative;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    width: 700px;
    height: 315px;
}
.code-snippet {
  margin:0 auto;
  display: block;
  float: right;
  padding: 40px 40px;
}
.run-code {
  background-color: #1fbad6;
  border: 3px solid #1fbad6;
  padding: 10px 20px;
  margin-top: 40px;
  margin-bottom: 40px;
  outline: none;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-align:center;
  text-decoration:none;
  color:#fff;
  border-radius:5px;
  float: right;
}
.video {
  position: relative;
  float: left;
  padding: 40px 40px;
}
/*.jsbin-embed {
  position: relative;
  float: right;
  width: 700px;
  height: 315px;
  padding: 40px 40px;
}*/
/* Initial menu */
.menu {
  background: #202024 url('http://s3.amazonaws.com/codecademy-content/courses/ltp2/img/uber/black-thread.png') repeat left top;
  left: -285px;  /* start off behind the scenes */
  height: 100%;
  position: fixed;
  width: 285px;
}
/* Basic styling */
.nav {
  /*background-image: url('http://s3.amazonaws.com/codecademy-content/courses/ltp2/img/uber/bg.png');*/
  background-color: #202020;
  height: 75px;
  -webkit-background-size: cover;
     -moz-background-size: cover;
       -o-background-size: cover;
          background-size: cover;
}
.menu ul {
  border-top: 1px solid #636366;
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu li {
  font-family: 'Open Sans', sans-serif;
  line-height: 45px;
  padding-bottom: 3px;
  padding-left: 20px;
  padding-top: 3px;
}
.main-cat {
  border-bottom: 1px solid #636366;
  color: #03A3EA;
}
.sub-cat li {
  font-family: 'Open Sans', sans-serif;
  line-height: 45px;
  padding-bottom: 3px;
  padding-left: 20px;
  padding-top: 3px;
}
.menu a {
  color: #fff;
  font-size: 15px;
  text-decoration: none;
  text-transform: uppercase;
}
.icon-close {
  cursor: pointer;
  padding-left: 10px;
  padding-top: 10px;
}
.icon-menu {
  color: #1fbad6;
  cursor: pointer;
  font-family: 'Open Sans', sans-serif;
  font-size: 20px;
  padding-bottom: 25px;
  padding-left: 25px;
  padding-top: 25px;
  text-decoration: none;
  text-transform: uppercase;
}
.icon-menu i {
  margin-right: 5px;
}
.login {
  position: relative;
  float: right;
  margin-top: 18px;
  margin-right: 20px;
  padding: 5px 15px;
  font-size: 18px;
  color: #1fbad6;
  background: none;
  border: 3px solid #1fbad6;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-align:center;
  text-decoration:none;
  border-radius:5px;
}
.sign-up {
  position: relative;
  float: right;
  margin-top: 18px;
  margin-right: 20px;
  padding: 5px 15px;
  font-size: 18px;
  color: #1fbad6;
  background: none;
  border: 3px solid #1fbad6;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-align:center;
  text-decoration:none;
  border-radius:5px;
}

/* ============ MODAL ========== */

.page-wrapper{
  width:100%;
  height: 100%;
/* //background:url(http://i.imgur.com/2ZgHKbQ.jpg) center no-repeat;
  //background-size:cover;*/
}
.blur{
  -webkit-filter: blur(5px);
  -moz-filter: blur(5px);
  -o-filter: blur(5px);
  -ms-filter: blur(5px);
  filter: blur(5px);
}
a.btn{
  width:150px;
  display:block;
  margin:-25px 0 0 -75px;
  padding:1em 0;
  position:absolute;
  top:50%; left:50%;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-align:center;
  text-decoration:none;
  color:#fff;
  border-radius:5px;
  background:rgba(217,67,86,1);
}
.modal-wrapper{
  width:100%;
  height:100%;
  position:fixed;
  top:0; left:0;
  background:rgba(64,64,64,1);
  visibility:hidden;
  opacity:0;
  -webkit-transition: all 0.25s ease-in-out;
  -moz-transition: all 0.25s ease-in-out;
  -o-transition: all 0.25s ease-in-out;
  transition: all 0.25s ease-in-out;
  z-index: 999;
}
.modal-wrapper.open{
  opacity:1;
  visibility:visible;
}
.modal{
  width:600px;
  height:400px;
  display:block;
  margin:50% 0 0 -300px;
  position:relative;
  top:50%; left:50%;
  background:#fff;
  opacity:0;
  -webkit-transition: all 0.5s ease-in-out;
  -moz-transition: all 0.5s ease-in-out;
  -o-transition: all 0.5s ease-in-out;
  transition: all 0.5s ease-in-out;
}
.modal-wrapper.open .modal{
  margin-top:-200px;
  opacity:1;
}
.head{
  width:90%;
  height:32px;
  padding:1.5em 5%;
  overflow:hidden;
  background:#01bce5;
}
.btn-close{
  width:32px;
  height:32px;
  display:block;
  float:right;
}
.btn-close::before, .btn-close::after{
  content:'';
  width:32px;
  height:6px;
  display:block;
  background:#fff;
}
.btn-close::before{
  margin-top:12px;
  -webkit-transform:rotate(45deg);
  -moz-transform:rotate(45deg);
  -o-transform:rotate(45deg);
  transform:rotate(45deg);
}
.btn-close::after{
  margin-top:-6px;
  -webkit-transform:rotate(-45deg);
  -moz-transform:rotate(-45deg);
  -o-transform:rotate(-45deg);
  transform:rotate(-45deg);
}
.content{
  padding:5%;
}
#username {
  width: 95%;
  background: none;
  border: 3px solid #1fbad6;
  padding: 12px;
  margin-bottom: 40px;
  outline: none;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-decoration:none;
  border-radius:5px;
  color: #202020;
}
#password {
  width: 95%;
  background: none;
  border: 3px solid #1fbad6;
  padding: 12px;
  margin-bottom: 40px;
  outline: none;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-decoration:none;
  border-radius:5px;
  color: #202020;
}
#login-submit {
  background-color: #1fbad6;
  border: 3px solid #1fbad6;
  padding: 10px 20px;
  margin-bottom: 40px;
  outline: none;
  font:1.125em 'Arial', sans-serif;
  font-weight:700;
  text-align:center;
  text-decoration:none;
  color:#fff;
  border-radius:5px;
  float: right;
}

JAVASCRIPT

var main = function() {
  /* Push the body and the nav over by 285px over */
  $('.icon-menu').click(function() {
    $('.menu').animate({
      left: "0px"
    }, 100);
    $('body').animate({
      left: "285px"
    }, 100);
  });
  /* Then push them back */
  $('.icon-close').click(function() {
    $('.menu').animate({
      left: "-285px"
    }, 100);
    $('body').animate({
      left: "0px"
    }, 100);
  });
};

/* ========== Evaluate Code ========== */
var app = {};
// go through the application and find every single instance of a div
// with the class of 'sandbox'
app.bootstrap = function() {
  var sandboxes = document.getElementsByClassName('sandbox');
  // for each sandbox, run the createSandbox function
  [].forEach.call(sandboxes, app.createSandbox);
};
// given a parent element, find the first textarea inside and
// create a sandbox around it
app.createSandbox = function(parent) {
  var textarea = parent.getElementsByTagName('textarea')[0],
      // create an instance of Sandbox using this textarea
      sandbox = Sandbox(textarea);
  parent.appendChild(sandbox.label);
};
// when the DOM loads bootstrap the application
window.addEventListener('load', app.bootstrap);
// Sandbox class
// This class is based around a textarea element, which will contain
// the code. However, it could just as easily be the DOM element for
// an Ace/Codemirror editor.
function Sandbox(textarea) {
  var sandbox = {};
  // create a label to show output
  sandbox.label = document.createElement('label');
  sandbox.label.setAttribute('class', 'output');
  sandbox.label.addEventListener('click', evaluate);
  // evaluate code whenever there is input into the textarea
  textarea.addEventListener('input', evaluate);
  sandbox.textarea = textarea;
  // initial resize and evaluation
  resize();
  evaluate();
  // resize to within the appropriate height for the textarea
  function resize() {
    var scrollHeight = textarea.scrollHeight;
    if(scrollHeight > Sandbox.MAX_HEIGHT) {
      height = Sandbox.MAX_HEIGHT;
    } else if(scrollHeight < Sandbox.MIN_HEIGHT) {
      height = Sandbox.MIN_HEIGHT;
    } else {
      height = scrollHeight;
    }
    textarea.style.height = height + 'px';
  }
  // evaluate the code within the textarea
  function evaluate() {
    // get the code
    var src = textarea.value,
    // create a console proxy (for logging to the label)
        console = Sandbox.consoleProxy(sandbox.label);
    // clear the output first
    sandbox.label.innerText = '';
    // try the eval and catch errors to send to the console
    try {
      /* jshint ignore:start */
      eval(src);
      /* jshint ignore:end */
    } catch(err) {
      console.error(err);
    }
  }
  return sandbox;
}
// config
Sandbox.MAX_HEIGHT = 500;
Sandbox.MIN_HEIGHT = 50;
// A function which spoofs the native console object, by writing
// text to output elements, rather than the dev tools console.
Sandbox.consoleProxy = function(element) {
  return {
    log: function(message) {
      message = [].join.call(arguments, ' ');
      element.innerText += (message + ''n');
      element.setAttribute('disabled', false);
      // write to the original console too
      console.log.apply(console, arguments);
    },
    error: function(message) {
      element.setAttribute('disabled', true);
      element.innerText = message;
    }
  };
};

/* ========== Modal  ========== */
$('.trigger').click(function() {
     $('.modal-wrapper').toggleClass('open');
    $('.page-wrapper').toggleClass('blur');
     return false;
  });
$(document).ready(main);

eval是运行代码的默认方式。由于其与安全漏洞的关系,它的声誉并不太好,然而,如果这不是一个问题,用户将在沙盒中运行自己的代码,这正是eval的设计初衷,而且它做得很好。

为Javascript实现一个简单的编译器是一项不小的任务,但是,如果你确信这是正确的方法,那么在解析和评估代码时,你可以利用esprima等开源项目来为你做一些繁重的工作。

我为过去的一个项目开发了一个通用代码评估小部件,只需使用<textarea>eval。它支持对控制台的日志和错误进行沙盒处理。浏览代码可能会有所帮助。