Using CodeMirror (readonly and editable code)

HTML

<h1>Using CodeMirror (readonly and editable code)</h1>
  
<p><a href="http://codemirror.net/mode/javascript/index.html">http://codemirror.net/mode/javascript/index.html</a></p>

    <link rel="stylesheet" href="http://codemirror.net/lib/codemirror.css">
    <script src="http://codemirror.net/lib/codemirror.js"></script>
    <script src="http://codemirror.net/addon/edit/matchbrackets.js"></script>
    <script src="http://codemirror.net/mode/javascript/javascript.js"></script>

    <h2>Readonly</h2>        
       
<div>
  <textarea rows="4" cols="50" id="codesnippet_readonly" name="codesnippet_readonly">
// Demo code (the actual new parser character stream implementation)

function StringStream(string) {
  this.pos = 0;
  this.string = string;
}

StringStream.prototype = {
  done: function() {return this.pos >= this.string.length;},
  peek: function() {return this.string.charAt(this.pos);},
  next: function() {
    if (this.pos &lt; this.string.length)
      return this.string.charAt(this.pos++);
  },
  eat: function(match) {
    var ch = this.string.charAt(this.pos);
    if (typeof match == "string") var ok = ch == match;
    else var ok = ch &amp;&amp; match.test ? match.test(ch) : match(ch);
    if (ok) {this.pos++; return ch;}
  },
  eatWhile: function(match) {
    var start = this.pos;
    while (this.eat(match));
    if (this.pos > start) return this.string.slice(start, this.pos);
  },
  backUp: function(n) {this.pos -= n;},
  column: function() {return this.pos;},
  eatSpace: function() {
    var start = this.pos;
    while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
    return this.pos - start;
  },
  match: function(pattern, consume, caseInsensitive) {
    if (typeof pattern == "string") {
      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
        if (consume !== false) this.pos += str.length;
        return true;
      }
    }
    else {
   ...

CSS

.cm__overlay {
  position: absolute;
  z-index: 10;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}
.cm__overlay--hidden {
  pointer-events: none;
  border: 1px solid red;
}

JavaScript

(function() {

function isChildOf(el, parent) {
	do {
  	el = el.parentNode;
  } while (el !== null && el !== parent && el !== document.body);
  return (el === parent);
}

var readOnlyCodeMirror = CodeMirror.fromTextArea(document.getElementById('codesnippet_readonly'), {
  mode: "javascript",
  theme: "default",
  lineNumbers: true,
  readOnly: true
});  

var editableCodeMirror = CodeMirror.fromTextArea(document.getElementById('codesnippet_editable'), {
  mode: "javascript",
  theme: "default",
  lineNumbers: true
});

var activeEditor = null, newActiveEditor = null;

for (let cm of document.querySelectorAll('.CodeMirror')) {
	let overlay = document.createElement('div');
  overlay.classList.add('cm__overlay');
  cm.insertBefore(overlay, cm.firstChild);
  overlay.addEventListener('click', function(event) {
  	overlay.classList.add('cm__overlay--hidden');
    if (activeEditor === null) {
    	activeEditor = cm;
    } else {
    	newActiveEditor = cm;
    }
  });
}

document.body.addEventListener('click', function(event) {
	if (activeEditor !== null && !isChildOf(event.target, activeEditor)) {
		activeEditor.firstChild.classList.remove('cm__overlay--hidden');
    activeEditor = null;
    if (newActiveEditor !== null) {
    	activeEditor = newActiveEditor;
      newActiveEditor = null;
    }
  }
});

}());