{"id":3063,"date":"2013-12-16T16:01:22","date_gmt":"2013-12-16T22:01:22","guid":{"rendered":"http:\/\/bililite.com\/blog\/?p=3063"},"modified":"2014-03-03T20:34:11","modified_gmt":"2014-03-04T02:34:11","slug":"simple-syntax-highlighting-editor-with-prism","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2013\/12\/16\/simple-syntax-highlighting-editor-with-prism\/","title":{"rendered":"Simple syntax-highlighting editor with Prism"},"content":{"rendered":"<a href=\"http:\/\/prismjs.com\">Prism<\/a> is a great Javascript syntax highlighter, and it's fast enough to be used on the fly for active editing; Lea Verou (the author) does this with <a href=\"http:\/\/dabblet.com\">Dabblet<\/a>. Combined with <a href=\"http:\/\/bililite.com\/blog\/2011\/01\/17\/cross-browser-text-ranges-and-selections\/\" title=\"Cross-Browser Text Ranges and Selections\"><code>bililiteRange<\/code><\/a> to handle some of the subtleties of dealing with <code class=\"language-html\" >contenteditable<\/code> elements, a syntax-highlighting editor is relatively easy.<\/p>\r\n<p>Simplest usage: <code class=\"language-javascript\" >bililiteRange.fancyText(element, Prism.highlightelement);<\/code>.<\/p>\r\n<p>See <a href=\"\/blog\/blogfiles\/prism\/prismeditor.html\">the demo<\/a>.<\/p>\r\n<p>See the <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/bililiteRange.fancytext.js\">code on github<\/a>.<\/p>\r\n<p>There's a fair amount of flakiness; <code class=\"language-javascript\" >contenteditable<\/code> elements are still handled inconsistently. Newlines are changed into actual elements (Firefox adds <code class=\"language-html\" >&lt;br&gt;<\/code>s; Chrome adds a whole new <code class=\"language-html\" >&lt;div&gt;<\/code>), even though a <code class=\"language-html\" >&lt;pre&gt;<\/code> doesn't need that; it would interpret newlines the way we want. So we have to intercept those and just insert the newline. Firefox insists on changing newlines into <code class=\"language-html\" >&lt;br&gt;<\/code>'s even in pasted text, and Chrome's clipboard implementation includes the carriage return character, so we have to intercept paste events and insert the text appropriately mangled.<\/p>\r\n<!--more-->\r\n<h3>Usage<\/h3>\r\n<pre><code class=\"language-javascript\" >bililiteRange.fancyText(pre-element,[highlighter]);<\/code><\/pre>\r\n<p>sets up <code class=\"language-html\" >pre-element<\/code> to be an editor; meaning that it listens for <code class=\"language-javascript\" >input<\/code> events and re-highlights the code each time. The element can be any element, though only <code class=\"language-html\" >&lt;pre&gt;<\/code> elements are targeted by the Prism CSS, and if you are writing code, whitespace is presumably important. So using <code class=\"language-html\" >&lt;pre&gt;<\/code> makes the most sense. In order to allow editing, it should have <code class=\"language-html\" >contenteditable<\/code> set, though that is not enforced.<\/p>\r\n<p><code class=\"language-javascript\" >highlighter<\/code> is a function <code class=\"language-javascript\" >highlighter(pre-element)<\/code> that does <em>something<\/em> to the element that leaves its underlying text unchanged; intended to be <code class=\"language-javascript\" >Prism.highlightelement<\/code>. If <code class=\"language-javascript\" >undefined<\/code>, just sets up the <code class=\"language-javascript\" >keydown<\/code> and <code class=\"language-javascript\" >paste<\/code> event listeners as above without doing anything else.<\/p>\r\n\r\n<pre><code class=\"language-javascript\" >var editor = bililiteRange.fancyText(textarea,[highlighter]);<\/code><\/pre>\r\n<p>Replaces <code class=\"language-html\" >textarea<\/code> with a <code class=\"language-html\" >&lt;pre&gt;<\/code>, calls <code class=\"language-javascript\" >Prism.editor<\/code> on that, and returns it. The original <code class=\"language-html\" >textarea<\/code> is deleted.<\/p>\r\n<pre><code class=\"language-javascript\" >var editor = bililiteRange.fancyText(pre-element, highlighter, threshold);<\/code><\/pre>\r\n<p>Even though Prism is fast, highlighting large texts with every keystroke can be slow. <code class=\"language-javascript\" >threshold<\/code> is the number of milliseconds to \"debounce\" the <code class=\"language-javascript\" >input<\/code> events; highlighting will only be done after that many milliseconds without any input.<\/p>\r\n\r\n<h3>Notes<\/h3>\r\n<p>Originally I had this with the Prism highlighter hard-wired in, as a Prism plugin. I realized that most of the code has nothing to do with Prism per se, so I could use dependency injection to make this a general-purpose highlighting editor; for instance, use<\/p>\r\n<pre><code class=\"language-javascript\" >bililiteRange.fancyText(el, function(el){\r\n  el.innerHTML = '&lt;span class=line&gt;'+el.innerHTML.split('\\n').join('&lt;\/span&gt;\\n&lt;span class=line&gt;')+'&lt;\/span&gt;';\r\n});<\/code><\/pre>\r\n<p>to add <a href=\"http:\/\/bililite.com\/blog\/2012\/08\/05\/line-numbering-in-pre-elements\/\" title=\"Line Numbering in <pre> Elements\">line numbers<\/a> with CSS.<\/p>\r\n\r\n<p><strong>Edited 2013-12-26:<\/strong> Prism requires an editable <code class=\"language-html\" >&lt;pre&gt;<\/code> or other element, not a <code class=\"language-html\" >&lt;textarea&gt;<\/code>. But that creates problems with progressive enhancement; I'd like my form to work without Javascript and <code class=\"language-html\" >&lt;pre&gt;<\/code>'s don't get submitted. So ideally, the original HTML should have a <code class=\"language-html\" >&lt;textarea&gt;<\/code> and the Javascript should replace it with a <code class=\"language-html\" >&lt;pre contenteditable&gt;<\/code>. In that case, clearly the Javascript is working and you can get the text out of the editor to add it to the submitted form.<\/p>\r\n<p><code class=\"language-javascript\" >Prism.editor<\/code> now does that. If the input element is a <code class=\"language-html\" >&lt;textarea&gt;<\/code>, it replaces it with a <code class=\"language-html\" >&lt;pre contenteditable&gt;<\/code> that has the same attributes (including <code class=\"language-html\" >id<\/code>) and returns the new element.<\/p>\r\n\r\n<p><strong>Edited 2013-12-17:<\/strong> added <code class=\"language-javascript\" >threshold<\/code> parameter.<\/p>","protected":false},"excerpt":{"rendered":"Prism is a great Javascript syntax highlighter, and it's fast enough to be used on the fly for active editing; Lea Verou (the author) does this with Dabblet. Combined with bililiteRange to handle some of the subtleties of dealing with contenteditable elements, a syntax-highlighting editor is relatively easy. Simplest usage: bililiteRange.fancyText(element, Prism.highlightelement);. See the demo. [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,10],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3063"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=3063"}],"version-history":[{"count":22,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3063\/revisions"}],"predecessor-version":[{"id":3303,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/3063\/revisions\/3303"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=3063"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=3063"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=3063"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}