December 30, 2016

Adding PrismJS Syntax Highlighter

So, I've been learning lots of new things and I'm ready to write a couple posts about my adventures with Angular 2. Before I do that, I wanted a syntax highlighter so I can use prettified code snippets. There was a pretty short list of requirements - theming options, line numbers, a copy button would be nice to have and simple installation. I installed GeSHi on a Drupal site a while back and while it will do just about everything, I didn't want to spend days reading the docs. I looked at several options and settled on Prism. It's not ideal, by the time I had it all looking close to how I wanted it, there were a lot of divs which I find hard to keep track of. Using <pre> tags also means you can't indent the code to match the rest of the page. But as Billy Crystal said "It's more important to look good than to feel good."

For basic usage and simple installation, the Prism site can roll a customized prism.js and prism.css. Include those in your page, enclose the code you want to show off in <pre> and <code> tags, add the appropriate class and your good to go. You can get the project from Github and load the pieces separately. It's handy trying things out and allows you to only load the language files you need for the particular page you're writing. This page includes the plugins Line Numbers, Remove initial line feed, and Unescaped Markup.

Adding A Copy Button with clipboardjs

Instead of using the Copy to Clipboard plugin from Prism, I went with clipboard.js. I haven't gotten the button style quite the way I want it yet, but it's still a pretty nifty addition to Prism. There are a number of ways to work with clipboard.js, the way I did it was:

Download and install clipboard.js (npm, bower or old fashioned .zip file).
Add the script to your web page
<script src="/_assets/js/clipboard.min.js"></script>

I decided to use a strategy where clipboard.js identifies the trigger(s) via DOM selector (in this case, the btn-copy class) and the target is set using data-clipboard-target in the trigger element (in this case <button>. The value of data-clipboard-target matches the target's element selector (in this case the id of the <pre> tag).

I also created a button with an image and used positioning to place it in the corner of the syntax highlighter box and set the initial opacity to 0.3. clipboard.js exposes custom events for success and error and I took advantage of that to indicate the copy was successfull.

Add the button
Finally, add the code for syntax highlighting.
It all comes together like this. This shows the Javascript I used to manage the button.

var clipboard = new Clipboard('.btn-copy');

clipboard.on('success', function(e) {

    var img = 'Copy to clipboard';

    e.trigger.blur();
    e.trigger.innerHTML = '';
    window.setTimeout(function () {
        e.trigger.innerHTML = img;
    }, 1000);

    e.clearSelection();
});

clipboard.on('error', function(e) {
    console.error('Action:', e.action);
    console.error('Trigger:', e.trigger);
    
    e.trigger.innerHTML = '';
    event.trigger.textContent = 'Press "Ctrl + C" to copy';
    window.setTimeout(function () {
        e.trigger.innerHTML = img;
    }, 2000);
});

Or Maybe...

I am hand coding the HTML for the web site and it seems like a lot of work to do the syntax highlighting, so I'll be looking for something easier. Github's embeddable Gists only require adding a script tag but lack the prettified syntax highlighting and copy button. Pretty or easy? Hmmm...

-- Laura