rich text copy


#1

I am working on enabling rich text copy with CM (firepad).

I set CM option inputStyle: “contenteditable”

copy then calls function onCopyCut(e)

however this code then replaces the clipboard with a plain text version.

I need an option for this code not to run. I checked this by simply remarking:
//on(div, “copy”, onCopyCut);
//on(div, “cut”, onCopyCut);
indeed I get rich text copy working great !

I tried to do this with supplying my own onCopy with preventDefault but of course this totally disables copying.

Perhaps an option can be added like “allowrichtextcopy” that will prevent onCopyCut from running/ replacing the clipboard ? this will be quite elegant as the browser will be doing all the html copy work. I can’t see any way of doing that without changing CM code.

Alternatively though less elegant, exposing the ‘onCopyCut’ function so as to allow removeEventListener(‘copy’’, onCopyCut) on the display div ?

Also, as a side note, another thing I noticed is that function onCopyCut(e) uses: e.clipboardData.setData(“text/plain”

I think this is not legal according to the standard. https://html.spec.whatwg.org/multipage/interaction.html#dom-datatransfer-setdata says:

“If the DataTransfer object is no longer associated with a drag data store, abort these steps. Nothing happens”

As far as I can tell, as onCopyCut is not a darg&drop operation so this should not work, and the correct way to copy data to the clipboard should be with execCommand(‘copy’)


#2

The content in the DOM can be different from the content in CodeMirror’s model (for example when code is replaced with a widget). Also, copying ‘rich’ HTML out of a text editor is usually the wrong behavior.

As for clipboardData, I haven’t studied the spec, but I know that this is how browsers are implementing direct clipboard access, and execCommand('copy') is a very different thing (it doesn’t allow us to control the data being put into the clipboard.)


#3

I am also looking for this functionality (copy to clipboard with markup) for a very long time. As my users want to copy the SQL code to Word with markup/syntax hightlighting. See my old post:

So plus 1 for this topic/feature request.

I tried this hack as follows but it doesn’t work with me:

    editor = CodeMirror.fromTextArea(document.getElementById("sqlCode"), {
      inputStyle: "contenteditable",
      autofocus: true,
      mode: "text/x-ELO-mssql",
      indentWithTabs: true,
      smartIndent: true,
      lineNumbers: true,
      matchBrackets : true,
      readOnly: true,
      tabSize: 2,
      styleSelectedText: true,   //for the addon mark-selection.js
      extraKeys: {"Ctrl-Space": "autocomplete"}, // To invoke the auto complete
      hint: CodeMirror.hint.sql,
      hintOptions: { }
    });

and commented out two lines in codemirror.js:

  //on(div, "copy", onCopyCut);
  //on(div, "cut", onCopyCut);

#4

I repicked this one up.
I have used https://github.com/lgarron/clipboard-polyfill to copy rich text to clipboard.
I catch the CTRL-C inside CodeMirror which triggers my copyRichText function.

This works!

<script type="text/javascript" src="js/clipboard-polyfill.promise.js"></script>

    editor = CodeMirror.fromTextArea(document.getElementById("sqlCode"), {
      autofocus: true,
      indentWithTabs: true,
      smartIndent: true,
      lineNumbers: true,
      matchBrackets : true,
      readOnly: true,
      tabSize: 2,
      styleSelectedText: true,   //for the addon mark-selection.js
      extraKeys: {
        "Ctrl-Space": "autocomplete", // To invoke the auto complete
        "Ctrl-C": copyRichText
      }, 
      hint: CodeMirror.hint.sql,
      hintOptions: { },
      inputStyle: "textarea" //as a workaround for mobile browser cursor location
    });


  function copyRichText() {
      console.log("copyRichText");
      var dt = new clipboard.DT();
      plainText=editor.getSelection("\r\n"); //be sure to use \r\n or else copy paste to Notepad (ugh!) doesn't have newlines!
      console.log(plainText);
      richText="<span style='text-transform: uppercase; color: #0000FF; '>select</span>";
      dt.setData("text/plain", plainText);
      dt.setData("text/html", richText);
      clipboard.write(dt);
  }

Whats still need to be done is to get style info for all content like this:

and apply it to the plain text as inline CSS markup.

In my CM I have syntax highlighted SQL code, so at least I want the color and text-transform CSS information so I can convert it to simple HTML/CSS for example:
<span style='text-transform: uppercase; color: #0000FF; '>select</span>


How to copy text with markup from codemirror editor to other application
#5

Here’s my working solution. It uses a small helper function to get all CSS styles for a classname, which I found at https://github.com/tjcafferkey/stylerjs
Since I needed to copy/paste SQL queries from my CodeMirror application named SQL School to for example Word I was only interested in the syntax highlighted info like color, bold, uppercase.

<script src="js/styler.js"></script>

  function copyRichText() {
      console.log("copyRichText");
      var dt = new clipboard.DT();
      
      plainText=editor.getSelection("\r\n"); //be sure to use \r\n or else copy paste to Notepad (ugh!) doesn't have newlines!
      
      richText="";
      for (i=0;i<editor.lineCount();i++) {
        lineTokens=editor.getLineTokens(i,true);
        for (t=0;t<lineTokens.length;t++) {
          token=lineTokens[t].string;
          token=token.replace(" ","&nbsp;");
          tokenType=lineTokens[t].type;
          if (tokenType!=null) { //token has codemirror style?
            cssTokenType = styler('.cm-'+tokenType).get(['text-transform', 'color', 'font-weight']);
            cssToken="text-transform: "+cssTokenType["text-transform"]+"; color: "+cssTokenType.color+"; font-weight: "+cssTokenType["font-weight"];
            richText+="<span style='"+cssToken+"'>"+token+"</span>";
          }
          else richText+="<span>"+token+"</span>";
        }
        richText+="<br>";
      }

      dt.setData("text/plain", plainText);
      dt.setData("text/html", richText);
      clipboard.write(dt);

      console.log(plainText);
      console.log(richText);
  }