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:
- Js error thrown on every post page claming:
Uncaught ReferenceError: SyntaxHighlighter is not defined - Tabs were removed inside code block.
- Auto text wrap not work at the long text lines.
Cause:
- The js error only happens if you turned on SyntaxHighlighter extension, and checked “collapse” option.
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.
<pre class="brush: csharp">html = reg.Replace(html, string.Empty).Trim();</pre>
Solution:
- 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.
<br /> <pre class="brush: html"><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:
<pre class="brush: csharp; collapse: true">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
}
- Open your site.master.cs, and comment out the line as below shows inside the Render method.
<pre class="brush: csharp;">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); } }</pre>
- Go to your BlogEngine.NET Control panel, navigate to “Settings/Custom code” pane, and paste the following code to the “head section”:
<pre class="brush: html"><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:
<br /><a href="https://raw.githubusercontent.com/Jeff-Tian/blogengine.net/master/Source/BlogEngine/BlogEngine.NET/App_Data/files/image_625.png"><img title="[Solved] SyntaxHighlighter issues after upgrading BlogEngine.NET from 2.6 to 2.7" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="[Solved] SyntaxHighlighter issues after upgrading BlogEngine.NET from 2.6 to 2.7" src="https://raw.githubusercontent.com/Jeff-Tian/blogengine.net/master/Source/BlogEngine/BlogEngine.NET/App_Data/files/image_thumb_344.png" width="618" height="212" /></a> </li>
- Now all issues related to SyntaxHighlighter are resolved!