[Solved] SyntaxHighlighter issues after upgrading BlogEngine.NET from 2.6 to 2.7

Problem:

I updated my BlogEngine.NET from 2.6 to 2.7, and everything goes well. But one day I found that the syntax highlighter were functioning horribly poor. They are:

  1. Js error thrown on every post page claming:
    Uncaught ReferenceError: SyntaxHighlighter is not defined
  2. Tabs were removed inside code block.
  3. Auto text wrap not work at the long text lines.

Cause:

  1. The js error only happens if you turned on SyntaxHighlighter extension, and checked “collapse” option.
    [Solved] SyntaxHighlighter issues after upgrading BlogEngine.NET from 2.6 to 2.7
    It will add a script line after the syntax highlighter js files were referenced like this:
    <script type="text/javascript" src="/Scripts/syntaxhighlighter/shCore.js" defer="defer" async="async"></script>
    <script type="text/javascript" src="/Scripts/syntaxhighlighter/shAutoloader.js" defer="defer" async="async"></script>
    <script type="text/javascript" src="/Scripts/syntaxhighlighter/shInit.js" defer="defer" async="async"></script><script type="text/javascript" defer="defer">
    	SyntaxHighlighter.defaults['collapse'] = true;
    });
    </script>
    I guess the defer=”defer” async=”async” attributes makes the execution order is unpredictable, so the last line `SyntaxHighlighter.defaults[‘collapse’] = true` actually executes before the shCore.js loaded.
  2. This is because in the site.master.cs, there is a line of code:
    html = reg.Replace(html, string.Empty).Trim();
  3. As be explained in this article, it is caused by the Syntax Highlighter 3.x separated the gutter and code so that when you copy code you won’t copy the line numbers. This is a good thing, but it forgot or gave up the auto line wrap functionality because it was hard to keep it.

Solution:

  1. Improve the SyntaxHighlighter extension so that it generates script codes like this to make sure when setting the SyntaxHighlighter.defaults, the SyntaxHighlighter had already been defined.
    <script type="text/javascript" src="/Scripts/syntaxhighlighter/shCore.js" defer="defer" async="async"></script>
    <script type="text/javascript" src="/Scripts/syntaxhighlighter/shAutoloader.js" defer="defer" async="async"></script>
    <script type="text/javascript" src="/Scripts/syntaxhighlighter/shInit.js" defer="defer" async="async"></script><script type="text/javascript" defer="defer">
    
            if (typeof executeOn === 'undefined') {
                window.executeOn = function (condition, func) {
                    var interval = setInterval(function () {
                        try {
                            var result = false;
    
                            if (typeof condition === "function") {
                                result = condition();
                            } else if (typeof condition === "string") {
                                result = eval(condition);
                            } else {
                                throw "argument 'condition' must be a bool function or a bool expression.";
                            }
    
                            if (result === true) {
                                clearInterval(interval);
                                func();
                            }
                        } catch (ex) {
                            clearInterval(interval);
                            throw ex;
                        }
                    }, 100);
                };
            }
    
    	// Make sure the SyntaxHighlighter has already been defined, otherwise wait a chunk of time
    	executeOn("typeof SyntaxHighlighter !== 'undefined' ", function() {
    		SyntaxHighlighter.defaults['collapse'] = true;
    
    		executeOn("document.readyState === 'loaded' || document.readyState === 'complete'", function() {
    			SyntaxHighlighter.all();
    	            });
    	});
    </script>
    The updated SyntaxHighlighter extension(SyntaxHighlighter.cs, if you don't have it, add it to the ~/App_Code/ folder in your BlogEngine.NET project) looks like this:
    using System.Text;
    using System.Web;
    using System.Web.UI.HtmlControls;
    using BlogEngine.Core;
    using BlogEngine.Core.Web.Controls;
    using BlogEngine.Core.Web.Extensions;
    using Page=System.Web.UI.Page;
    using System.Collections.Generic;
    using System;
    
    [Extension("Adds <a target=\"_new\" href=\"http://alexgorbatchev.com/wiki/SyntaxHighlighter\">Alex Gorbatchev's</a> source code formatter", "2.5.2", "<a target=\"_new\" href=\"http://dotnetblogengine.net/\">BlogEngine.NET</a>")]
    public class SyntaxHighlighter
    {
        #region Private members
        private const string ExtensionName = "SyntaxHighlighter";
        static protected Dictionary<Guid, ExtensionSettings> _blogsOptions = new Dictionary<Guid, ExtensionSettings>();
        static protected Dictionary<Guid, ExtensionSettings> _blogsThemes = new Dictionary<Guid, ExtensionSettings>();
        #endregion
    
        /// <summary>
        ///     The sync root.
        /// </summary>
        private static readonly object syncRoot = new object();
    
        private static ExtensionSettings Options
        {
            get
            {
                Guid blogId = Blog.CurrentInstance.Id;
                ExtensionSettings options = null;
                _blogsOptions.TryGetValue(blogId, out options);
    
                if (options == null)
                {
                    lock (syncRoot)
                    {
                        _blogsOptions.TryGetValue(blogId, out options);
    
                        if (options == null)
                        {
                            // Initializes
                            //   (1) Options
                            //   (3) Themees
                            // for the current blog instance.
    
                            // options
                            options = new ExtensionSettings("Options");
                            options.IsScalar = true;
                            options.Help = OptionsHelp();
    
                            options.AddParameter("cdnScriptsPath", "CDN Script Path", 250, false);
                            options.AddParameter("cdnStylesPath", "CDN Styles Path", 250, false);
                            options.AddParameter("gutter", "Gutter");
                            options.AddParameter("smart-tabs", "Smart tabs");
                            options.AddParameter("auto-links", "Auto links");
                            options.AddParameter("collapse", "Collapse");
                            options.AddParameter("tab-size", "Tab size");
                            options.AddParameter("toolbar", "Toolbar");
    
                            options.AddValue("cdnScriptsPath", ""); // "http://alexgorbatchev.com.s3.amazonaws.com/pub/sh/3.0.83/scripts/");
                            options.AddValue("cdnStylesPath", ""); // "http://alexgorbatchev.com.s3.amazonaws.com/pub/sh/3.0.83/styles/");
                            options.AddValue("gutter", true);
                            options.AddValue("smart-tabs", true);
                            options.AddValue("auto-links", true);
                            options.AddValue("collapse", false);
                            options.AddValue("tab-size", 4);
                            options.AddValue("toolbar", true);
    
                            _blogsOptions[blogId] = ExtensionManager.InitSettings(ExtensionName, options);
    
                            // themes
                            ExtensionSettings themes = new ExtensionSettings("Themes");
                            themes.IsScalar = true;
                            themes.AddParameter("SelectedTheme", "Themes", 20, false, false, ParameterType.ListBox);
                            themes.AddValue("SelectedTheme", new string[] { "Default", "Django", "Eclipse", "Emacs", "FadeToGrey", "MDUltra", "Midnight", "Dark" }, "Default");
                            _blogsThemes[blogId] = ExtensionManager.InitSettings(ExtensionName, themes);
                        }
                    }
                }
    
                return options;
            }
        }
    
        private static ExtensionSettings Themes
        {
            get
            {
                // by invoking the "Options" property getter, we are ensuring
                // that an entry is put into _blogsThemes for the current blog instance.
                ExtensionSettings options = Options;
                return _blogsThemes[Blog.CurrentInstance.Id];
            }
        }
    
        static SyntaxHighlighter()
        {
            Post.Serving += AddSyntaxHighlighter;
            InitSettings();
        }
    
        private static void AddSyntaxHighlighter(object sender, ServingEventArgs e)
        {
            if (!ExtensionManager.ExtensionEnabled("SyntaxHighlighter"))
                return;
    
    		if(e.Location == ServingLocation.Feed) 
                return;
    
            // if no code blocks on the page - don't bother
            if (!e.Body.ToLowerInvariant().Contains("<pre class=\"brush:"))
                return;
    	
            HttpContext context = HttpContext.Current;
    		
            Page page = (Page)context.CurrentHandler;
    
            if ((context.CurrentHandler is Page == false) || (context.Items[ExtensionName] != null))
            {
                return;
            }
    
            AddCssStyles(page);
            AddJavaScripts(page);
            AddOptions(page);
    
            context.Items[ExtensionName] = 1;
        }
    
        private static void AddCssStyles(Page page)
        {
            AddStylesheet("shCore.css", page);
    
            if (Themes != null)
            {
                switch (Themes.GetSingleValue("SelectedTheme"))
                {
                    case "Django":
                        AddStylesheet("shThemeDjango.css", page);
                        break;
                    case "Eclipse":
                        AddStylesheet("shThemeEclipse.css", page);
                        break;
                    case "Emacs":
                        AddStylesheet("shThemeEmacs.css", page);
                        break;
                    case "FadeToGrey":
                        AddStylesheet("shThemeFadeToGrey.css", page);
                        break;
                    case "MDUltra":
                        AddStylesheet("shThemeMDUltra.css", page);
                        break;
                    case "Midnight":
                        AddStylesheet("shThemeMidnight.css", page);
                        break;
                    case "Dark":
                        AddStylesheet("shThemeRDark.css", page);
                        break;
                    default:
                        AddStylesheet("shThemeDefault.css", page);
                        break;
                }
            }       
        }
    
        private static void AddJavaScripts(Page page)
        {
            if (BlogSettings.Instance.EnableOptimization)
            {
                BlogEngine.Core.Web.Scripting.Helpers.AddScript(
                    page, string.Format("{0}Scripts/highlighter", Utils.ApplicationRelativeWebRoot), false, true, true);
            }
            else
            {
                BlogEngine.Core.Web.Scripting.Helpers.AddScript(
                    page, string.Format("{0}Scripts/syntaxhighlighter/shCore.js", Utils.ApplicationRelativeWebRoot), false, true, true);
                BlogEngine.Core.Web.Scripting.Helpers.AddScript(
                    page, string.Format("{0}Scripts/syntaxhighlighter/shAutoloader.js", Utils.ApplicationRelativeWebRoot), false, true, true);
                BlogEngine.Core.Web.Scripting.Helpers.AddScript(
                    page, string.Format("{0}Scripts/syntaxhighlighter/shInit.js", Utils.ApplicationRelativeWebRoot), false, true, true);
            }
        }
    
        #region Script/Style adding
    
        private static void AddJavaScript(string src, Page page)
        {
            page.ClientScript.RegisterStartupScript(page.GetType(), src, String.Format("<script type=\"text/javascript\" src=\"{0}\"></script>", GetUrl(ScriptsFolder(), src)));
        }
    
        private static void AddStylesheet(string href, Page page)
        {
            HtmlLink css = new HtmlLink();
            css.Attributes["type"] = "text/css";
            css.Attributes["rel"] = "stylesheet";
            css.Attributes["href"] = GetUrl(StylesFolder(), href);
            //begin: jeff@zizhujy.com
            try
            {
                if (page != null && page.Header != null && page.Header.Controls != null)
                    page.Header.Controls.Add(css);
            }
            catch (NullReferenceException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            //end: jeff@zizhujy.com
        }
    
        private static void AddOptions(Page page)
        {
            StringBuilder sb = new StringBuilder();
    
            var executeOn = @"
            if (typeof executeOn === 'undefined') {
                window.executeOn = function (condition, func) {
                    var interval = setInterval(function () {
                        try {
                            var result = false;
    
                            if (typeof condition === ""function"") {
                                result = condition();
                            } else if (typeof condition === ""string"") {
                                result = eval(condition);
                            } else {
                                throw ""argument 'condition' must be a bool function or a bool expression."";
                            }
    
                            if (result === true) {
                                clearInterval(interval);
                                func();
                            }
                        } catch (ex) {
                            clearInterval(interval);
                            throw ex;
                        }
                    }, 100);
                };
            }";
    
            
            sb.AppendLine("<script type=\"text/javascript\" defer=\"defer\">");
            sb.AppendLine(executeOn);
    
            sb.AppendLine(@"executeOn(""typeof SyntaxHighlighter !== 'undefined' "", function() {");
    
            // add not-default options
            if (Options != null)
            {
                if(Options.GetSingleValue("gutter").ToLowerInvariant() == "false")
                    sb.AppendLine(GetOption("gutter"));
    
                if (Options.GetSingleValue("smart-tabs").ToLowerInvariant() == "false")
                    sb.AppendLine(GetOption("smart-tabs"));
    
                if (Options.GetSingleValue("auto-links").ToLowerInvariant() == "false")
                    sb.AppendLine(GetOption("auto-links"));
    
                if (Options.GetSingleValue("collapse").ToLowerInvariant() == "true")
                    sb.AppendLine(GetOption("collapse"));
                
                if (Options.GetSingleValue("toolbar").ToLowerInvariant() == "false")
                    sb.AppendLine(GetOption("toolbar"));
    
                if (Options.GetSingleValue("tab-size") != "4")
                    sb.AppendLine(GetOption("tab-size"));
            }  
            
            //sb.AppendLine("\tSyntaxHighlighter.all();");
    
            sb.AppendLine(@"
                executeOn(""document.readyState === 'loaded' || document.readyState === 'complete'"", function() {
                    SyntaxHighlighter.all();
                });");
    
            sb.AppendLine(@"});");
            sb.AppendLine("</script>");
            page.ClientScript.RegisterStartupScript(page.GetType(), "SyntaxHighlighter", sb.ToString(), false);
        }
    
        private static string GetUrl(string folder, string url)
        {
            string s = HttpContext.Current.Server.UrlPathEncode(string.Format("{0}{1}", folder, url));
            if (!folder.ToLowerInvariant().Contains("http:") && !folder.ToLowerInvariant().Contains("https://"))
                s = Utils.ApplicationRelativeWebRoot + s;
            return s;
        }
        
        #endregion
    
        #region Private methods
    
        private static void InitSettings()
        {
            // call Options getter so default settings are loaded on application start.
            var s = Options;
        }
    
        static string OptionsHelp()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<p>This extension implements excellent Alex Gorbatchev's syntax highlighter JS library for source code formatting. Please refer to <a target=\"_new\" href=\"http://alexgorbatchev.com/wiki/SyntaxHighlighter:Usage\">this site</a> for usage.</p>");
            sb.AppendLine("<p><b>cdnScriptsPath</b>: Allows you to load the SyntaxHighlighter script from a CDN. Leave empty for local files</p>");
            sb.AppendLine("<p><b>cdnStylesPath</b>: Allows you to load the SyntaxHighlighter styles from a CDN. Leave empty for local files</p>");
            sb.AppendLine("<p><b>auto-links</b>: Allows you to turn detection of links in the highlighted element on and off. If the option is turned off, URLs won't be clickable.</p>");
            sb.AppendLine("<p><b>collapse</b>: Allows you to force highlighted elements on the page to be collapsed by default.</p>");
            sb.AppendLine("<p><b>gutter</b>:	Allows you to turn gutter with line numbers on and off.</p>");
            sb.AppendLine("<p><b>smart-tabs</b>:	Allows you to turn smart tabs feature on and off.</p>");
            sb.AppendLine("<p><b>tab-size</b>: Allows you to adjust tab size.</p>");
            sb.AppendLine("<p><b>toolbar</b>: Toggles toolbar on/off.</p>");
            sb.AppendLine("<p><a target=\"_new\" href=\"http://alexgorbatchev.com/wiki/SyntaxHighlighter:Configuration\">more...</a></p>");
            return sb.ToString();
        }
    
        static string GetOption(string opt)
        {
            if (Options != null)
            {
                string pattern = "\tSyntaxHighlighter.defaults['{0}'] = {1};";
    
                string val = Options.GetSingleValue(opt).ToLowerInvariant();
                return string.Format(pattern, opt, val);
            }
    
            return "";
        }
    
        static string ScriptsFolder()
        {
            if (Options != null)
            {
                if (!String.IsNullOrEmpty(Options.GetSingleValue("cdnScriptsPath")))
                    return Options.GetSingleValue("cdnScriptsPath");
                else
                    return "Scripts/syntaxhighlighter/";
            }
            return "";
        }
    
        static string StylesFolder()
        {
            if (Options != null)
            {
                if (!String.IsNullOrEmpty(Options.GetSingleValue("cdnStylesPath")))
                    return Options.GetSingleValue("cdnStylesPath");
                else
                    return "Styles/syntaxhighlighter/";
            }
            return "";
        }
    
        #endregion
    }
  2. Open your site.master.cs, and comment out the line as below shows inside the Render method.
    protected override void Render(HtmlTextWriter writer)
        {
            using (HtmlTextWriter htmlwriter = new HtmlTextWriter(new System.IO.StringWriter()))
            {
                base.Render(htmlwriter);
                string html = htmlwriter.InnerWriter.ToString();
    
                //html = reg.Replace(html, string.Empty).Trim();
    
                writer.Write(html);
            }
        }
  3. Go to your BlogEngine.NET Control panel, navigate to “Settings/Custom code” pane, and paste the following code to the “head section”:
    <style type="text/css">
        body .syntaxhighlighter .line {
            white-space: pre-wrap !important;
        }
    </style>
    
    <script type="text/javascript" id="line-word-wrap-syntax-highlighter">
        $(function () {
            // Line wrap back
            var shLineWrap = function () {
                // Fix by Jeff Tian that make it work with the collapsed setting
                $('.syntaxhighlighter:not(.collapsed)').each(function () {
                    // Fetch
                    var $sh = $(this),
                        $gutter = $sh.find('td.gutter'),
                        $code = $sh.find('td.code')
                    ;
                    // Cycle through lines
                    $gutter.children('.line').each(function (i) {
                        // Fetch
                        var $gutterLine = $(this),
                            $codeLine = $code.find('.line:nth-child(' + (i + 1) + ')')
                        ;
                        //alert($gutterLine);
                        // Fetch height
                        var height = $codeLine.height() || 0;
                        if (!height) {
                            height = 'auto';
                        }
                        else {
                            height = height += 'px';
                            //alert(height);
                        }
                        // Copy height over
                        $gutterLine.attr('style', 'height: ' + height + ' !important'); // fix by Edi, for JQuery 1.7+ under Firefox 15.0
                    });
                });
            };
    
            // Line wrap back when syntax highlighter has done it's stuff
            var shLineWrapWhenReady = function () {
                if ($('.syntaxhighlighter').length === 0) {
                    setTimeout(shLineWrapWhenReady, 100);
                }
                else {
                    shLineWrap();
                    // Fix by Jeff Tian that make it work with the collapsed setting
                    if ($(".syntaxhighlighter.collapsed").length > 0) {
                        setTimeout(shLineWrapWhenReady, 100);
                    }
                }
            };
    
            // Fire
            shLineWrapWhenReady();
        });
    </script>
    Screenshot:
    [Solved] SyntaxHighlighter issues after upgrading BlogEngine.NET from 2.6 to 2.7
  4. Now all issues related to SyntaxHighlighter are resolved!

Add comment

Loading