In Part 1, I discussed an approach to handling Tumblr Theme Variables that do not have corresponding {block:Foobar} Variables to wrap code to only render when the variable {Foobar} exists.
I used the {PlayCount} variable (rendered in Audio and Video blocks) as my example. In the end, I came up with the following solution:
<script>
if ( "{PlayCount}" ) {
document.write("<span>played {PlayCount} times!</span>");
}
</script>
… which works fine (in this case), even though it is an ugly solution.
But Sometimes Things are More Complicated
Now I would like to talk about the {LinkURL} variable, which is rendered inside of Photo posts. This is the so-called “Click-through URL” for a Photo.
Now, Tumblr has the absolute strangest feature for this variable: a corresponding {LinkOpenTag} and {LinkCloseTag}, which renders an HTML anchor tag (<a href=”…… “>) for the {LinkURL} only if it exists.
Well, that might be fine for some people. But what if I want to modify that <a> tag? Add a class name to it? A title attribute? Etc.
Basically - I find {LinkOpenTag} and {LinkCloseTag} completely bizarre and completely out of place with the rest of the Tumblr Theme Template system.
And again - we have the same problem: There is no {block:LinkURL} variable to tell you when {LinkURL} exists.
Trying to Solve the Same Problem the Same Way
So, of course I tried to use the same approach as before:
<script>
if ( "{LinkURL}" ) {
document.write('<a class="foo" href="{LinkURL}"> .. </a>');
}
</script>
And yes, this probably works 99% of the time.
But I don’t really trust a URL can be inserted directly into a Javascript chunk in this way. A URL could possibly break the Javascript String in this example, if it has not been escaped properly.
Does Tumblr escape the {LinkURL} properly? I don’t know — but seeing how many other things they manage to mess up, I’m just going to guess and say No, just to be on the safe side. Maybe it’s escaped fine for HTML, but not JavaScript. Who knows.
So…Tumblr’s Variable Transformations to the Rescue!
Wait - again you ask? I thought you dismissed that in Part 1.
I did dismiss it.
I showed how {JSPlayCount} does not do what the Tumblr Documentation says it should - namely return a Javascript-safe string representation of {PlayCount} wrapped in quotes. [verbatim from the documention]
Instead it renders nothing, and not wrapped in quotes. Fine.
But it Gets Weirder.
But I really want to make sure my {LinkURL} is safe to include in my script.
So I tried playing around with {JSLinkURL}. Now, I figured it would behave like {PlayCount} — namely that if it didn’t exist, then {JSLinkURL} should render nothing.
So I had a stroke of genius:
<script>
if ( [ {JSLinkURL} ].length) {
document.write('.....');
}
</script>
Notice the brackets create a list. Now, an empty list [] is a valid JavaScript expression, so if {JSLinkURL} does not render anything at all, this yields:
if ( [ ].length ) { .... }
Which is valid Javascript, and will return False, if {LinkURL} does not exist.
Problem Solved — Right?
Oh, wrong, wrong again.
I thought it was working.
And then I came upon some curious posts, which for some reason or other, have no {LinkURL}but where {JSLinkURL} actually does render an empty JavaScript string “” !
But then again, it is Tumblr. Sometimes it works, sometimes it doesn’t.
In these weird cases, where there really is no LinkURL, but {JSLinkURL} renders an empty string, you end up with this:
if ( [ '' ].length ) { ... }
Which unfortunately, returns True in Javascript. @#$!
So, revising the code a little:
if ( [ {JSLinkUrl} ].length && [ {JSLinkUrl} ][0] ) {
....
}
This works in both cases. If {JSLinkUrl} renders nothing, it yields:
if ( [].length && [][0] ) { ... }
Which is fine because JavaScript has short-circuit evaluation with the && operator.
If {JSLinkURL} renders an empty string, it yields:
if ( [ '' ].length && [ '' ][0] ) { ... }
Which is also fine, because although [ ” ].length is True, [ ” ][0] is False in Javascript, since ” evaluates to False.
Putting it all Together:
<script>
if ( [ {JSLinkUrl} ].length && [ {JSLinkUrl} ][0] ) {
document.write('<a class="foo" title="bar" href="' +
encodeURI( {JSLinkUrl} ) + '"> ... </a>');
}
</script>
Edit 2/24/2012:
Using what I uncovered in Part 3, I now recommend wrapping {JSLinkURL} with encodeURI because this variable is not properly escaped / URL encoded!
This is a fairly robust way to emulate a {block:LinkURL}, while ensuring that:
- The JavaScript raises no errors
- The LinkURL you insert is safe to insert directly into the JavaScript code.
It’s ugly. It’s a hack. But it works.