Sunday, July 8, 2012

OScript syntax highlighting in pure JavaScript


If you want to display OScript source code in your blog article you may want to render it with syntax highlighting. Modern text editors offer it to improve the code readability and thus increase the productivity. While you could prepare a ready HTML code with font and colour styling, it becomes inconvenient when writing more articles. Why not being able to write raw code and see it "beautified", just like in the IDE?

One of JavaScript syntax highlighting engines available is Alex Gorbatchev's SyntaxHighlihter. It applies styling to raw source code on the client side in the browser, like this:

// Gets a Livelink document.
function DAPINODE GetDocument(Integer id)
  DAPINODE node = DAPI.GetNodeByID(id, -2000, false)
  if !IsError(node) and node.pSubType == $Document
#ifdef DEBUG
    Echo(Str.Format("Document %1: %2", \
                             node.pID, node.pName))
#endif
    return node
  end
end

The source code of the text above looks the same as in the Livelink Builder; just the pre element has special attributes to turn on the styling:

<pre class="brush: oscript;" id="oscript-sample">
// Gets a Livelink document.
function DAPINODE GetDocument(Integer id)
  DAPINODE node = DAPI.GetNodeByID(id, -2000, false)
  if !IsError(node) and node.pSubType == $Document
#ifdef DEBUG
    Echo(Str.Format("Document %1: %2", \
                    node.pID, node.pName))
#endif
    return node
  end
end
</pre>

If you wish the same effect for your articles, follow this:

  1. Make yourself familiar how to include the SyntaxHighlighter on a web site or choose instructions according to you publishing portal. If you use Blogger, there is a way how to encapsulate syntax highlighting in a single post to avoid modifying the blog page template.
  2. Grab the brush shBrushOScript.js which adds the OScript support and optionally the stylesheet shThemeVisualStudioLike.css which defines the style you can see above. Put them to a public host of your choice to be able to refer to them from your blog.
  3. Update your SyntaxHighlighter integration (either in the page template or in the single post) to refer to those two files.

Actually, the Livelink Builder IDE doesn't provide such syntax highlighting yet... If you're interested you can learn about a workaround for the time being.

Enjoy!

Syntax highlighting of source code samples on Blogger - for a single article only


I wanted to integrate Alex Gorbatchev's SyntaxHighlihter to a blog article on Blogger. There is a multitude of instructions available but all of them have one thing in common: modification of the blog page template to include all necessary scripts and stylesheets. It will enable the syntax highlighter on every page but also make every page always load it.

Well, if you don't use a code excerpt in every post, why should your every page load the syntax highlighting engine? It should be possible to enable it just for a particular post, shouldn't it?

Yes, it is possible - with an additional JavaScript code in your blog post. If you are familiar with the usual blog template modification the following idea adds just inserting stylesheets and scripts to the page head on-demand. When you're writing a blog article with some source code you'll enter the HTML editing mode, create a <script type="text/javascript">...</script> element in and put the following code to its body: (a double-click within the code will select it all to enable putting it to clipboard easily)

(function() {

function loadCss(url){
  var head = document.getElementsByTagName("head")[0]
  var styles = head.getElementsByTagName("link")
  for (var i = 0; i < styles.length; ++i)
    if (styles[i].href.indexOf(url) >= 0)
      return
  var style = document.createElement("link")
  style.rel = "stylesheet"
  style.type = "text/css"
  style.href = url
  head.appendChild(style)
}

function loadJs(url, after){
  var head = document.getElementsByTagName("head")[0]
  var scripts = head.getElementsByTagName("script")
  var script
  for (var i = 0; i < scripts.length; ++i) {
    var current = scripts[i]
    if (current.src.indexOf(url) >= 0) {
      script = current
      break
    }
  }
  if (!script) {
    script = document.createElement('script')
    script.type = "text/javascript"
  }
  if (after)
    if (script.readyState)
      if (script.readyState == "loaded" ||
          script.readyState == "complete") {
        after()
      } else {
        var oldreadystatechange = script.onreadystatechange
        script.onreadystatechange = function() {
          if (script.readyState == "loaded" ||
              script.readyState == "complete")
            after()
          if (oldreadystatechange)
            oldreadystatechange()
        }
      }
    else {
      if (script.getAttribute("data-loaded")) {
        after()
      } else {
        script.async = false
        var oldload = script.onload
        script.onload = function() {
          script.setAttribute("data-loaded", "true")
          after()
          if (oldload)
            oldload()
        }
      }
    }
  if (!script.src) {
    script.src = url
    head.appendChild(script)
  }
}

function highlight(tags) {
  SyntaxHighlighter.config.bloggerMode = true
  for (var i = 0; i < tags.length; ++i) {
    var tag = document.getElementById(tags[i])
    SyntaxHighlighter.highlight(undefined, tag)
  }
}

// Use a public hosting site of your choice instead of "...".
loadCss(".../shCore.css")
loadCss(".../shThemeDefault.css")
loadJs(".../shCore.js", function() {
  // Add other brushes to this chain as necessary.
  loadJs(".../shBrushJScript.js", function() {
    // List IDs of elements to highlight their content.
    highlight([ "js-sample1", "js-sample2" ])
  })
})

}());

Notice the three comments above - those places you are supposed to modify according to your blog article.

1. The stylesheets and scripts must be loaded from a publicly accessible URL. You can use Alex's hosting but if you have a possibility to put the files onto your site you should prefer it; the bandwith of Alex's site is not unlimited... For example, if you have your pages on Google Sites, which may be likely because both Blogger and Sites are provided by Google, you can upload the SyntaxHighlighter support there and refer to them from the script, for example:

https://sites.google.com/site/your_name/css/shCore.css
https://sites.google.com/site/your_name/css/shThemeDefault.css
https://sites.google.com/site/your_name/js/shCore.js
https://sites.google.com/site/your_name/js/shBrushJScript.js

2. If you need multiple languages (brushes) in your article you'll load them in a chain of calls one after another. The following script is loaded first when the previous one has been finished; it would be done so anyway by the browser but it allows essentially the last expression that starts the highlighting to be executed after all scripts were processed: (see the latest list of built-in brushes and also additional ones)

loadJs(".../shCore.js", function() {           // Common
  loadJs(".../shBrushJScript.js", function() { // JavaScript
    loadJs(".../shBrushCss.js", function() {   // CSS
      // Highlight the code; all brushes are loaded now.
      highlight([ "js-sample1", "js-sample2" ])
    })
  })
})

3. When placing a code excerpt to the article, in addition to the usual attributes, you'll give the pre element a unique ID that you include in the array of element IDs sent to the function highlight as shown above:

<pre class="brush: js;" id="js-sample1">
...
</pre>

When writing HTML or XML samples, an on-line HTML encoder may help you to escape special characters. (The brush for HTML is the shBrushXml.js, btw.) You can consider minifying the JavaScript code above and the external scripts you include by the Google Closure Compiler or a similar tool. There are tools for minifying CSS files too, even on-line. For example, this is the partially minified code I've used for my articles:

(function() {
function loadCss(d){for(var b=document.getElementsByTagName("head")[0],c=b.getElementsByTagName("link"),e=0;e<c.length;++e)if(0<=c[e].href.indexOf(d))return;c=document.createElement("link");c.rel="stylesheet";c.type="text/css";c.href=d;b.appendChild(c)}
function loadJs(d,b){for(var c=document.getElementsByTagName("head")[0],e=c.getElementsByTagName("script"),a,f=0;f<e.length;++f){var g=e[f];if(0<=g.src.indexOf(d)){a=g;break}}if(!a)a=document.createElement("script"),a.type="text/javascript";if(b)if(a.readyState)if("loaded"==a.readyState||"complete"==a.readyState)b();else{var h=a.onreadystatechange;a.onreadystatechange=function(){("loaded"==a.readyState||"complete"==a.readyState)&&b();h&&h()}}else if(a.getAttribute("data-loaded"))b();else{a.async=
!1;var i=a.onload;a.onload=function(){a.setAttribute("data-loaded","true");b();i&&i()}}if(!a.src)a.src=d,c.appendChild(a)}function highlight(d){SyntaxHighlighter.config.bloggerMode=!0;for(var b=0;b<d.length;++b){var c=document.getElementById(d[b]);SyntaxHighlighter.highlight(void 0,c)}};

loadCss("https://sites.google.com/site/your_name/styles/shCore.min.css")
loadCss("https://sites.google.com/site/your_name/styles/shThemeVisualStudioLike.min.css")
loadJs("https://sites.google.com/site/your_name/scripts/shCore.min.js", function() {
  loadJs("https://sites.google.com/site/your_name/scripts/shBrushJScript.min.js", function() {
    loadJs("https://sites.google.com/site/your_name/scripts/shBrushXml.min.js", function() {
      highlight(["js-sh-loader", "js-sh-chain", "html-sh-sample", "js-sh-real"])
    })
  })
})

}());

Loading the syntax highlighting support only for articles that actually need it takes a little more care than having it loaded on all pages automatically, indeed. But you'll have your readers download less data and make fewer HTTP requests when browsing your regular pages. And when you replace you blog template with another one you won't need bother editing it to include your modifications; your blog articles will be encapsulated with all extras they need to be rendered.

Enjoy!

Friday, July 6, 2012

Comfortable editing of OScript source code

Greg Griffiths published on his web site an interesting list of text editor extensions that enable more comfortable editing of OScript sources than inside the Livelink Builder IDE (see the original Greg's page for the most recent list):

EditorAdd On
TextPadAdd On    Instructions
Notepad++Add On
Edit PlusAdd On
ScintillaAdd On

The add-ons provide syntax highlighting and/or code block folding for OScript sources. Modern editing components offer more convenience than the Livelink Builder IDE and even though you'd have to paste the resulting code back to the Livelink Builder editor when finished, you might still feel more comfortable than when typing in the Livelink Builder editor all the time.

I got interested because I actually do this. I use SciTE (a programmer's notepad, originally a demonstration application for the Scintilla editing component) when working with OScript sources, mainly for three things:
  1. Write longer scripts. Autocompletion and calltip help improves the productivity.
  2. Review source code. Code structure emphasized by the font style is easier to go through.
  3. Default viewer for opening *.osx files from Perforce Visual Client. I find myself looking and comparing code from other branches pretty often; code block folding and calltip help are very useful to understand foreign code.
SciTE (actually Scintilla) offers advanced editing features, search/replace, block operations and other features you find in editors of modern IDEs like Microsoft Visual Studio. Specifically for OScript sources, there is syntax highlighing, code block folding and calltip help for the language itself and also for objects from the Livelink core API.

If you like the screenshots below and want the same viewing/editing experience you'll need to download at least the version 3.2.0 of SciTE that has OScript support built in. Linux users can simply install the latest SciTE from the package repository of their distribution. Additionally you'll grab and install the OScript API description from the SciTE customization page to get autocompletion and calltip help.

Enjoy!



Thursday, July 7, 2011

dirsync - directory content synchronization tool

I released an older project of mine - dirsync. It is a command-line tool to synchronize content (files and folders) of two directories. Why another tool when rsync and his relatives have been already there? Dirsync makes sure that the free space on the target drive does not get exhausted if it is almost full and many files were changed on the source drive. It performs the deletion operations at first to make place for the new files to be copied there.

For example, how to synchronize photos from the working drive to a backup one recursively including subfolders:

  dirsync D:\Photos E:\Photos

Firstly, files are deleted from the target directory that cannot be found in the source one. Then existing files get updated if their source counterparts have newer last modification time. Finally, new files are copied to the target drive.

What to do if there are two root directories on a single drive to synchronize and you still make sure all files to be deleted are processed first? Specify the operations to perform selectively:

  dirsync -o:delete D:\Movies E:\Movies
  dirsync -o:delete D:\Music E:\Music
  dirsync -o:update,create D:\Movies E:\Movies
  dirsync -o:update,create D:\Music E:\Music

Sources of the tool are maintained in the github repository.