Estoy trabajando en un editor de Python y me gustaría agregar una función para insertar texto en la posición del signo de intercalación en un área de texto de CodeMirror.

Hay una serie de imágenes en las que se puede hacer clic. Cuando se hace clic en uno, se guarda el atributo alt de esa imagen, y luego, cuando se hace clic nuevamente dentro del área de texto, se copia en la posición del mouse (un violín de demostración: https://jsfiddle.net/t0k7yp7n/1/)

Aquí hay un script para la parte de inserción de texto:

selected = '';

$('.insert').click(function() {
    console.log($(this).attr('alt'));
    selected = $(this).attr('alt');
});

$('#textbox').click(function() {
    insertAtCaret('textbox', selected)
        // Clear the selection so it isn't copied repeatedly
    selected = '';
});

function insertAtCaret(areaId, text) {
    var txtarea = document.getElementById(areaId);
    var scrollPos = txtarea.scrollTop;
    var strPos = 0;
    var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ?
        "ff" : (document.selection ? "ie" : false));
    if (br == "ie") {
        txtarea.focus();
        var range = document.selection.createRange();
        range.moveStart('character', -txtarea.value.length);
        strPos = range.text.length;
    } else if (br == "ff") strPos = txtarea.selectionStart;

    var front = (txtarea.value).substring(0, strPos);
    var back = (txtarea.value).substring(strPos, txtarea.value.length);
    txtarea.value = front + text + back;
    strPos = strPos + text.length;
    if (br == "ie") {
        txtarea.focus();
        var range = document.selection.createRange();
        range.moveStart('character', -txtarea.value.length);
        range.moveStart('character', strPos);
        range.moveEnd('character', 0);
        range.select();
    } else if (br == "ff") {
        txtarea.selectionStart = strPos;
        txtarea.selectionEnd = strPos;
        txtarea.focus();
    }
    txtarea.scrollTop = scrollPos;
}

Y aquí está la parte del área de texto de CodeMirror:

var editor;

//<![CDATA[
window.onload = function() {
    editor = CodeMirror.fromTextArea(document.getElementById('textbox'), {
      mode: {
        name: "python",
        version: 2,
        singleLineStringErrors: false
      },
      lineNumbers: true,
      indentUnit: 4
    });
  } //]]>
<!DOCTYPE html>
<html>

<head>

  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js" type="text/javascript"></script>
  <script src="http://www.skulpt.org/static/skulpt.min.js" type="text/javascript"></script>
  <script src="http://www.skulpt.org/static/skulpt-stdlib.js" type="text/javascript"></script>
  <script src="https://www.cs.princeton.edu/~dp6/CodeMirror/lib/codemirror.js" type="text/javascript"></script>
  <script src="https://www.cs.princeton.edu/~dp6/CodeMirror/mode/python/python.js" type="text/javascript"></script>
  <script src="skulpt-codemirror.js" type="text/javascript"></script>
  <script src="load-save-py.js" type="text/javascript"></script>
  <script src="insert.js" type="text/javascript"></script>
  <link href="https://www.cs.princeton.edu/~dp6/CodeMirror/lib/codemirror.css" rel="stylesheet" type="text/css">
  <title>Python Editor</title>
</head>

<body>
  Filename:
  <input id="inputFileNameToSaveAs">
  <button onclick="saveTextAsFile()">Save</button>
  <br>
  <input type="file" id="fileToLoad">
  <button onclick="loadFileAsText()">Open</button>
  <br>
  <br>
  <a href="#!">
    <img class="insert" alt="#1">
    <img class="insert" alt="#2">
    <img class="insert" alt="#3">
  </a>
  <br>
  <br>
  <textarea id="textbox" name="textbox"></textarea>
  <br>
  <button onclick="runit()" type="button">Run</button>
  <pre id="dynamicframe"></pre>
  <div id="canvas"></div>
</body>

</html>

Sin embargo, cuando las pongo juntas en un archivo, cuando hago clic en las imágenes, sus alt no se copian en el área de texto. ¿Por qué es esto y cómo lo soluciono?

7
user5832974 23 ago. 2016 a las 09:22

2 respuestas

La mejor respuesta

Al usar CodeMirror, su <textarea /> será reemplazado visualmente por un editor proporcionado por CodeMirror y la mayor parte de su código relativo a su <textarea /> no se podrá utilizar tal como está.

Lo que sucede en el fondo es que su <textarea /> real primero se marcará con un estilo display: none;. No se muestra, no se activará ningún evento vinculado en el <textarea />. Entonces, CodeMirror agregará su propio código al DOM para mostrar un nuevo editor en la posición de su <textarea /> que ahora no se muestra.

Por ejemplo, el código HTML para un editor CodeMirror recién inicializado con la cadena 'Hello World' escrita se vería así:

<div class="CodeMirror-lines">
    <div style="position: relative; outline: none;">
        <div class="CodeMirror-measure">
            <div style="width: 50px; height: 50px; overflow-x: scroll;"></div>
        </div>
        <div class="CodeMirror-measure"></div>
        <div style="position: relative; z-index: 1;"></div>
        <div class="CodeMirror-cursors">
            <div class="CodeMirror-cursor" style="left: 74px; top: 0px; height: 13px;">&nbsp;</div>
        </div>
        <div class="CodeMirror-code">
            <div style="position: relative;">
                <div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;">
                    <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">1</div>
                </div><pre><span style="padding-right: 0.1px;"><span class="cm-variable">Hello</span> <span class="cm-variable">World</span></span></pre></div>
        </div>
    </div>
</div>

Tu <textarea /> ya no se usa.

CodeMirror proporciona de forma nativa una API de programación que se puede utilizar para hacer lo que desee. Básicamente, los pasos necesarios son:

  • Comprueba cuando el editor está enfocado.
  • Cuando esté enfocado, compruebe si se ha hecho clic en una imagen anteriormente y si hay un texto selected disponible (el alt de la imagen).
  • Si es así, inserte el texto selected (el alt de la imagen) en la posición actual.

El código JavaScript asociado a estos pasos se vería así:

// Listen to the editor focus events.
editor.on('focus', function () {
  // Only insert if a value has been previously selected.
  if (selected.length > 0) {
    // Fetch the current CodeMirror document.
    var doc = editor.getDoc();

    // Insert the text at the cursor position.
    doc.replaceSelection(selected);

    // Clear the selection so it isn't copied repeatedly.
    selected = '';
  }
});

Puede consultar un ejemplo funcional en este JSFiddle.

2
HiDeo 25 ago. 2016 a las 08:32

En resumen, este código reemplazará la selección actual del espejo de código (o la posición actual del cursor cuando no hay ninguna selección) con un texto:

codemirror.doc.replaceSelection('text to replace');

1
yaya 19 mar. 2020 a las 20:53