decorateCooked in theme vs HighlighJS

I’m using decorateCooked in my site’s theme to post-process HighlightJS code blocks for a particular language. It is working when ``` is used to indicate code in a post but It is failing when indentation is used to include code.

In the successful case the HighligtJS classes are present before my decorateCooked code is invoked and I can use JQuery to find code blocks carrying my language class (‘applescript’). In the failing case, it appears that HighlightJS is applying its magic after my plugin code runs because the HighlighJS classes (hljs applescript) are missing. But these classes are present when I later inspect the loaded page.

Here’s my code which is defined in the </HEAD> portion of my theme:

<script type="text/discourse-plugin" version="0.1">
    if (navigator.appVersion.indexOf('Mac') >= 0) { // Only do this on the Mac
        api.decorateCooked(
            $elem => {
                console.log($elem.find('code'));
                console.log($elem.find('code').attr('class'));
                $elem.find('.lang-applescript, .applescript').filter(function () {
		            return $(this).parents('.d-editor-preview').length < 1; // Don't do this in editor previews
            	}).each(function(index) {
   		            var src = encodeURIComponent(this.innerText);

        		    $( '<a class="widget-button btn btn-default" href="sdapplescript://com.apple.scripteditor?action=new&script=' + src + '">Open in Script Debugger</a>' ).insertAfter($(this).parent());
        	    });
            },
            { id: 'applescript-decorator' }
        );
    }
</script>

Looking at the console log, I see that I’m finding the <code> blocks in my posts, but the class attribute is reported as undefined.

Is there a way of having my decorateCooked code run after HighlightJS has done its thing?

1 Like

I’ve found a workaround for this problem using MutationObserver but I’m not sure this is the best approach to the problem:

<script type="text/discourse-plugin" version="0.1">
    if (navigator.appVersion.indexOf('Mac') >= 0) { // Only do this on the Mac
        if (typeof myObserver == 'undefined') {
            var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
            var myObserver = new MutationObserver(function (mutations) {
                mutations.forEach(function (mutation) {
                    var target = $(mutation.target);
                    
                    if (target.hasClass('applescript')) {
   		                var src = encodeURIComponent(mutation.target.innerText);

        		        $('<a class="widget-button btn btn-default" href="sdapplescript://com.apple.scripteditor?action=new&script=' + src + '">Open in Script Debugger</a>' ).insertAfter(target.parent());
                    }
                });
            });
            var obsConfig = { childList: false, characterData: false, attributes: true, subtree: false };
        }

        api.decorateCooked(
            $elem => {
                $elem.find('.lang-applescript, .applescript').filter(function () {
		            return $(this).parents('.d-editor-preview').length < 1; // Don't do this in editor previews
            	}).each(function(index) {
   		            var src = encodeURIComponent(this.innerText);

        		    $('<a class="widget-button btn btn-default" href="sdapplescript://com.apple.scripteditor?action=new&script=' + src + '">Open in Script Debugger</a>' ).insertAfter($(this).parent());
        	    });
        	    
        	    //  Deal with the case where HighlightJS is applied *after* decorateCooked is called.  This happens for
        	    //  code blocks introduced using indentation rather than ```.
                $elem.find('code:not(.applescript,.lang-applescript)').filter(function () {
		            return $(this).parents('.d-editor-preview').length < 1; // Don't do this in editor previews
            	}).each(function () {
                    myObserver.observe(this, obsConfig);
                });
            },
            { id: 'applescript-decorator', onlyStream: true }
        );
    }
</script>
1 Like

I just had the same challenge, so I added a new API feature to handle this use case: api.registerHighlightJSPlugin.

You can pass highlightjs function based plugins to it, which is documented at Plugin API — highlight.js 11.6.0 documentation

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.