Of late, I’ve needed a robust JavaScript template engine that doesn’t use eval (and preferably not with). Also, I’ve always liked Liquid.
So, two plus two equals… JavaScript! Hah, you thought it was four, didn’t you? Well, that’s what you get for thinking.
Yeah, long story short, I ported Liquid to JavaScript. You can grab it from github (where else?) here: http://github.com/darthapo/liquid.js
I’m calling this version 0.1. It’s not battle tested yet, but I know it’ll work using Firefox 3+, Safari 3+, and Adobe Air 1.1.
So what are the differences between running the Ruby version and the JavaScript version? Well, as far as the templates themselves, nothing. It’s a full port, so all of Liquid’s default tags and filters are supported in Liquid.js. I even added a placeholder function that you can implement yourself (based on your own needs) to support the ‘include’ tag.
var elem = $path;
ifelem
return eleminnerHTML;
else
return path +" can't be found.";
// Or throw and error, or whatever you want...
var src = "{% include 'myOtherTemplate' with current_user %}";
var tmpl = Liquidparse src ;
alert tmplrender current_user:'M@' ;
An easy way to include template in a page is to use script elements, like this:
<!-- Browsers ignore script blocks with an unrecognized type. -->
<!-- Makes for pretty good inline template storage. -->
Currently, Liquid.js requires MooTools. Some of the things I’d like to polish up:
TaskTHIS is up and running again. Sorry for the downtime, but since I host it for you guys for free, I don't think you can complain too much. ![]()
In the process, it's moved URLs as well. You can now find it at:
http://taskthis.elucidata-apps.com
The old URL (taskthis.darthapo.com) should redirect you there automatically.
I had to convert TaskTHIS from Rails 1.1 to Rails 2.1, which was not too fun. Especially since it had already been converted from pre-1.0 to 1.1.
Anyway, it's probably still a little rough around the edges, so if you find any bugs please add a post in the support forum.
I'm in the process of shuffling servers around, so a lot of the apps/sites I've created will be down whilst I make the move.
Some things are already back up: ZooDotCom and Lil Monstas. Others, like Maelstrom and TaskTHIS are not.
In fact, TaskTHIS may take a little while longer to bring back up. I'm going to update it to Rails 2. That having been said, I did make a backup of the database before bringing it down. So when it returns all of your data will still be there.
Also, I've noticed that I post a lot fewer blog posts these days since I generally vent on my Twitter account, I wind up having not too much left to say. Funny that.
Sterling Code can help you get from design to finished HTML quickly.
We specialize in creating semantic, SEO optimized XHTML and HTML for rich emails. Offering competitive rates and quick turn-around, check us out!
Yesterday we talked about how to make nifty selectable toolbars like this:

Now let's look at the finishing touch for our Preferences window; Animating the panel changes. We'll be flying through this at a pretty good clip, but don't worry. I'll provide the full source for your inspection.
First off, let's add some new outlets to our window controller:
ib_outlets :generalPrefsView,
:advancedPrefsView
Now in Interface Builder, we'll create the views for each preference pane by dragging Custom Views from the Library onto our Preferences.nib.
Note: Be sure to drop the Custom Views on the main nib window in IB, not on the Preferences NSWindow. Your project (in IB) should look something like this:

Hook up the outlets to the new views, and edit your preference panels to your heart's desire. From here, we go back to the code.
Tip: Be sure to set the auto-sizing on your preference panels (the NSViews) so that it matches the NSWindow's contentView.

Next up are some helper methods for our window controller. I won't spend too much time explaining these, but they're pretty straight forward.
case tag
when 0: [@generalPrefsView, "General"]
when 1: [@advancedPrefsView, "Advanced"]
end
end
#viewForTag actually returns our NSView and a title string.
newFrameRect = window.frameRectForContentRect(view.frame)
oldFrameRect = window.frame
newSize = newFrameRect.size
oldSize = oldFrameRect.size
frame = window.frame
frame.size = newSize
frame.origin.y = frame.origin.y - (newSize.height - oldSize.height)
frame
end
#newFrameForNewContentView calculates the new frame rectangle for the window based on the new view (preference pane).
Now we're ready to fill out our selectPrefPanel action:
ib_action :selectPrefPanel do |sender|
tag = sender.tag
view, title = self.viewForTag(tag)
previousView, prevTitle = self.viewForTag(@currentViewTag)
@currentViewTag = tag
newFrame = self.newFrameForNewContentView(view)
window.title = " Preferences"
# Using an animation grouping because we may be changing the duration
NSAnimationContext.beginGrouping
# Call the animator instead of the view / window directly
window.contentView.animator.replaceSubview_with(previousView, view)
window.animator.setFrame_display newFrame, true
NSAnimationContext.endGrouping
end
Right on! Now we setup the initial pane when the window loads:
window.setContentSize @generalPrefsView.frame.size
window.contentView.addSubview @generalPrefsView
window.title = "General Preferences"
@currentViewTag = 0
# Will use CoreAnimation for the panel changes:
window.contentView.wantsLayer = true
end
That pretty much does it. Now you have a professional looking preferences window. So enough of those dang blasted NSTabViews!
Here's the completed PreferencesController.rb. Or, you can download the full Xcode project. (Requires Leopard, Xcode 3, and Interface Builder 3)
Happy coding!