Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / weblog / Javascript for Tridion scripting

Javascript for Tridion scripting

Posted by Dominic Cronin at Oct 09, 2010 05:30 PM |
Filed under: , ,

During the recent Tridion MVP summit, the subject of Javascript came up. That's not surprising, as we were busy with building some GUI extensions to run on the SDL Tridion 2011 CTP, which is a pretty Javascript intensive activity. Most Tridion people reading this won't be too surprised if I say that Javascript isn't really in the comfort zone for many of us. We've done templating in VBScript, and although Tridion has always supported Javascript (or more strictly, JScript) for templating, we've always avoided it. Tridion has never shipped a JavaScript version of the default templates, and I suspect that's why, very early on, VBScript became the VHS to JavaScript's BetaMax. Most people accept that Betamax lost to VHS despite being technologically better, and for the sake of this discussion I'm going to ignore the dissenting opinions that have surfaced recently. In my view JavaScript is a superior technology to VBScript in many ways, but VBScript achieved critical mass in our world, and that's that. Or is it?

Well to start with, I'm not about to suggest that anyone should start to write Tridion component templates in JavaScript. These days, if you're defecting from VBScript templating, you'll be going to a .NET language; probably C#, and I'm all in favour of that. On the other hand, for many of us, JavaScript clearly has a place in our future. If you're writing web applications these days, a solid grasp of js is essential, and as noted already, if you want to customise the Tridion GUI, you'll be elbow-deep in the stuff before you get anything useful done.

For myself, I started using Javascript for simple update scripts and the like quite some time ago, and this mostly means running the scripts from the command line on the server using the cscript processor built-in to Windows. (These days, I also use PowerShell scripting for some of the more ad hoc work, but that's another story.)

The first obvious point about the language is that it has several styles or idioms in common use. For those of you that are familiar with libraries such as JQuery, you'll know that the style can be very similar to what you might encounter in languages with a functional flavour. To gain expertise in this style, I'd strongly recommend jumping in the deep end with John Resig's "Learning Advanced Javascript". (It's a pretty deep deep end. Note to self: have another go soon, and try to understand it this time!)

The other two idioms I'd mention are the object-oriented style, and what I can only describe as Microsoft style. For the object-oriented style, and how to achieve it, you can do no better than to view Doug Crockford's lecture series hosted at Yahoo. I suppose the best place to learn the Microsoft style is MSDN. In the days of the Atlas project, Microsoft tried to use JavaScript as just another layer in the ASP.NET stack, and with some success. For Tridion guys, this style is most notable because some of this flavour is to be seen in the Tridion GUI - or at least that's how it appears to me based on a fairly peremptory poke around. (I'd love someone to tell me if if I'm wrong. Really! Although comments won't show up, as I have to moderate to prevent spam. That's my only moderation criterion, though. If you take the trouble to write a real comment, I'll publish it.)

I hope to return to to the subject of Tridion and Javascript in future posts, but for now, I'd just like to start with a simple example of why it works for me. But please be kind; I'm not a Resig or a Crockford.

It's fairly often useful to be able to recurse through a folder structure and process each item within a given hierarchy. Usually, the processing involves two parts. Firstly, filtering: am I interested in this item or not? Secondly, for the items I'm interested in, I want to run some code, either to report on the item or to alter it in some way. This pattern comes up a lot, and for a lot of the small- to medium-scale jobs, the recurse-filter-process logic is a significant part of the work. Maybe the actual payload only amounts to a couple of lines of code. If you had a simple way to re-use the recurse-filter-process part, you could do a lot of quick-and-dirty jobs, erm... pretty quickly.

In Javascript, I can paste the following function in to a template or a script file, and the recurse-filter-process part is done:

function recurseItems( orgItem, filter, process){
  var items = orgItem.GetItems();
  var eItems = new Enumerator(items);
  for (;!eItems.atEnd();eItems.moveNext()){
    var itemType = eItems.item().ItemType;
    if (itemType === TDSDefines.ItemType.Folder || itemType === TDSDefines.ItemType.StructureGroup){
        recurseItems(eItems.item(), filter, process)
    } else {
      if (filter(eItems.item())) {
          process(eItems.item());
      }
    }
  }
}

I'm quite sure you could tidy this up a bit, but whatever... What makes this so straightforward to re-use is the fact that in JavaScript, functions are first-class objects, and it's very easy to pass functions as arguments to another function, and invoke them from within that function. The recurseItems() function expects to be passed an organizational Item as its first argument. (OK - as written, this won't work for categories, or for the sake of argument, publications, but bear with me...)

The "filter", and "process" arguments are functions.

Let's say I want to list all the components in a given hierarchy. I could write something like this (by which I mean this code isn't tested, it's for illustration purposes, right?):

function isComponent(item){return item.ItemType === TDSDefines.ItemType.Component;}
function outputTitle(item){WScript.Echo(item.Title + "\n";))
var topFolder = tdse.GetObject("tcm:1-1234-2", TDSDefines.OpenMode.View);
recurseItems(topFolder, isComponent, outputTitle);

So with 4 lines of code, I've listed the items I'm interested in. Not bad, eh? OK - I cheated. But how? Take a look at the attached file TDSDefines.js. It's a port of the standard TDSDefines constants to JavaScript. The cheating part is that I instantiate the "tdse" object in there, which somewhat breaks the purity of having a TDSDefines file, but you're always going to want tdse, so wtf not? Anyway - this file is what allows me to type things like "TDSDefines.ItemType.Component", or TDSDefines.OpenMode.View, instead of 16, or 1. JavaScript lends itself very well to this kind of nested data structure, in ways that VBScript would struggle with.

Assuming you are using the cscript host on your Tridion server, and that TDSDefines.js is in the same directory as your script, you'll need to use a couple of lines of code to import your "defines".

    var fso = new ActiveXObject("Scripting.FileSystemObject");
    eval(fso.OpenTextFile(fso.BuildPath(fso.GetParentFolderName(WScript.ScriptFullName), "TDSDefines.js"), 1).ReadAll());

Inside Tridion, of course, TDSDefines.js just becomes a template building block, and gets included in the normal way.

Funnily enough, I never got round to porting the default template code to JavaScript, and now, presumably, I never will. All the same, using JavaScript for this kind of work has allowed me to practice and get more familiar with the language, and to have a toolkit that allows for very, very fast creation of quick-and-dirty recursion scripts among others. It doesn't end there. JavaScript, or perhaps JSON, allows you to take a script-as-data approach which will get you to your desired result much quicker than, for example, having to write scripts that crunch through XML data files. Perhaps that would make a good subject for a future post.

In the meantime, I hope this gives you yet another excuse to hone your JavaScript skills. Those skills will definitely come in useful.

Filed under: , ,