Sunday, April 15, 2007

Colorize code samples for your blog post


Problem Statement
CSS-based colorer?
Colorer on sourceforge.net
Back to CSS-based colorer
Wow, this is an SOA and AJAX :)
New behavior
Test First
Colorer-Bridge Implementation
P.S.

Problem Statement

When working on the article about my journey with WebDav technology I faced with a challenge to colorize code samples that I wanted to share. I wanted that the source code in my article be not only shadowed gray and printed with Courier font, but really presented the way you would expect it to be presented by an IDE.

I saw a few development forums doing this behind the scenes and I thought I wanted the same capability on my blog. The challenge is that my blog page is hosted on Blogger so server-side solution doesn’t work. Unless Blogger provides custom tags to do this, that is :). The only thing I’m allowed to do is to customize HTML of my postings.

CSS-based colorer?

I was thinking how great it would be to have a CSS-based solution so you can put your source code into HTML <SPAN> element, define a few style attributes, and let it automagically colorize everything. wow… In theory, you could build a language recognizer in JavaScript (port ANTLR), add some functions around it to convert Java-Java into HTML-Java, wrap it into DHTML behavior, and here you go. Your colorer CSS style is ready for you. The only thing is, it would take me months (or years) to cope with it :) I knew this is not an option so I started as I always start when I want to see what’s out there – Google search. I was hoping to find a colorer library with a command line interface or a Java API so I could run it or build a small program to colorize my samples offline and upload the result to my blog. This sounded much more realistic :)

Colorer on sourceforge.net

First search result brought me to the http://colorer.sourceforge.net. This was exactly what I was looking for and much more. Colorer guys created web interface where you can colorize your code fragments on the fly. This was the kicker ! Go to http://colorer.sourceforge.net/php and check it out.

Compare this:

public static String doSomething(final Integer value) {
throw new Exception("sample");
}

To this:

public static String doSomething(final Integer value) {
throw new Exception("sample");
}

Or this:

<xsl:template match="/">
<xsl:apply-templates select="node/element/@myAttr"/>
</xsl:template>

To this:

<xsl:template match="/">
<xsl:apply-templates select="node/element/@myAttr"/>
</xsl:template>


Back to CSS-based colorer

colorer-take5 solved my original problem. Now I had all my code samples colorized and nice-looking. But what about CSS-based colorer? I still wanted to make one. And now I knew how to do this :) The solution presented is really a toy that I don’t believe is useful but it works well and it seems to be very elegant.

Wow, this is an SOA and AJAX :)

I decided to use colorer web interface as a service provider. I needed to build a capability to call it, process results it returns, and wrap it into the DHTML behavior so that an HTML element can consume the service and colorize its content.



Colorer service API is just a form POST to http://colorer.sourceforge.net/php/generator.php with number of parameters. Now we need that the HTML element with some content to colorize defines new behavior that would let it consume the colorer service,

New behavior

HTML element powered by the new colorer-bridge behavior should define a few custom properties:


  • language – to tell colorer what type of language grammar to recognize

  • encoding – since colorer-take5 supports input and output encoding it’s a good idea to allow using it

  • style – to be able to use multiple color schemas supported by colorer-take5

  • mode – to define whether element should colorize itself automatically or defer this until external command to colorize received



When properties defined, the element needs to be granted with actions to actually call the colorer – colorize() - and parse the results – applyColorerResults(). Looks like we are all set to wrap it all together.

Test First

Let’s look at the HTML sample that uses the colorer-bridge component. It will be much easier to comprehend the idea by looking at how it’s used:


<html xmlns:c>
<?IMPORT namespace="c" implementation="colorer-bridge.htc"?>
<head>
<title>Colorer API Bridge Sample</title>
</head>
<body>
<!-- used as element behavior -->
<c:colorer sourceLang="jScript">
<pre>
function myFunction() {
this.property = value;
}
</pre>
</c:colorer>

<!-- another technique knows as attached behaviour -->
<pre style="behavior:url('colorer-bridge.htc')" sourceLang="xml">
<root>
<element attribute="value"/>
</root>
</pre>
</body>
</html>


As you see you can define your custom <c:colorer> element or attach new behavior to the <PRE> element. The reason we need PRE anyway is to preserve whitespaces and line breaks. Otherwise Explorer consumes everything before the component can grab it.

Colorer-Bridge Implementation

Ok. And this is our guy. The colorer-bridge.htc that does the job:


<PUBLIC:COMPONENT tagName="colorer">

<PUBLIC:PROPERTY ID="propLanguage" NAME="sourceLang"
GET="getLanguage" PUT="setLanguage"/>
<PUBLIC:PROPERTY ID="propEncodingIn" NAME="encodingIn"
GET="getEncodingIn" PUT="setEncodingIn"/>
<PUBLIC:PROPERTY ID="propEncodingOut" NAME="encodingOut"
GET="getEncodingOut" PUT="setEncodingOut"/>
<PUBLIC:PROPERTY ID="propStyle" NAME="style"
GET="getStyle" PUT="setStyle"/>
<PUBLIC:PROPERTY ID="propMode" NAME="mode"
GET="getMode" PUT="setMode"/>

<PUBLIC:ATTACH EVENT="oncontentready"
FOR="element" ONEVENT="doInit()" />

<PUBLIC:METHOD NAME="colorize" />
<PUBLIC:METHOD NAME="rollback" />

<SCRIPT>
var sWaitMessage = "Please whait while colorizing...";
var sApiUrl = "http://colorer.sourceforge.net/php/generator.php";

var sOriginalContent; // code to colorize
var oCodeSnippetContainer; // HTML element with the code
var oColorerAPI; // XmlHttpRequest object

function doInit() {
// see if applied to a PRE element
if (element.tagName == 'PRE') {
oCodeSnippetContainer = element;

// or if PRE element defined as a child
} else {
oCodeSnippetContainer =
element.getElementsByTagName("PRE")[0];
}

// remember original content to support rollback
sOriginalContent = oCodeSnippetContainer.innerHTML;
oCodeSnippetContainer.innerText = sWaitMessage;

// initialize Colorer 'API'
oColorerAPI = new ActiveXObject("Microsoft.XMLHTTP");
oColorerAPI.open("POST", sApiUrl, true);
oColorerAPI.onreadystatechange = applyColorerResponse;

if (element.mode == "auto") {
colorize();
}
}

function colorize() {
// put together POST request
var sRequest = "";
sRequest += "i_encoding=" + element.encodingIn;
sRequest += "&o_encoding=" + element.encodingOut;
sRequest += "&hrd_color=" + element.style;
sRequest += "&type=" + element.sourceLang;
sRequest += "&file_content=" + sOriginalContent;

oColorerAPI.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
oColorerAPI.send(sRequest);
}

function applyColorerResponse() {
if (oColorerAPI.readyState == 4 && oColorerAPI.status == 200) {
// replace original code fragment with its colorized version
oCodeSnippetContainer.innerHTML = oColorerAPI.responseText;
}
}

function rollback() {
oCodeSnippetContainer.innerHTML = sOriginalContent;
}

//----------------- PROPERTIES --------------------
var sLanguage = ""; // Autodetect
var sEncodingIn = "ISO-8859-1";
var sEncodingOut = "ISO-8859-1";
var sStyle = "default"; // white
var sMode = "auto";

function getLanguage() {
return sLanguage;
}

function setLanguage(sValue) {
sLanguage = sValue;
propLanguage.fireChange();
}

// other getters and setters skipped

</SCRIPT>

<BODY>
</BODY>

</PUBLIC:COMPONENT>


That’s it. Enjoy!

P.S.

P.S. Many thanks to the Colorer team @ sourceforge.
P.P.S. Do you know the name of the guy who renamed DHTML into AJAX?

4 comments:

Anonymous said...

Very nice script, man!

As for AJAX, you know that this is not only JS+XML, the key point here is "Anisochronous". It means fully new technology of data processing. You can use Flash or Flex and make Async-calls to server and render content on a front-end without page reloadings and it will be AJAX in a wide sense.

So, the guys who developed this term wanted to create a new brand rather than create new name of the existent things I guess :)

Max Gulevich

Pavel Veller said...

you're right about asynch processing. but it's been there for quite a while before AJAX term came up. XmlHttpRequest has been available since IE5 (2000?). iframe-based posts (document.frame.src ="") and further onload actions following by DHTML manipulations were available for even longer. So nothing really new. It just took time for the technique to get popular and the technology behind it to grow, adapt, and spread.

I just wonder who was the first guy refering to AJAX as a new brand name :)

Anonymous said...

Yes, in 2002 it was a project for SAPxMA where prototype was developed as DHTML/HTC/ASP solution by me and Slava. Now it calls AJAX :)


As for history - look at Wikipedia:
"The first use of the term in public was by Jesse James Garrett in February 2005"

http://en.wikipedia.org/wiki/AJAX

Max Gulevich

Anonymous said...

Keep up the good work.