Thursday, October 14, 2010

Dynamically adding jQuery to header only if it hasn't been loaded

Some time ago I did some research on Javascript and some magical, lesser known features that modern javascript libraries leverage to their great advantage.

It's not really all that magical when you get right down to it, but it is really cool.

Take this for instance:

        if(typeof jQuery == 'undefined'){
            var headID = document.getElementsByTagName("head")[0];        
            var jsNode = document.createElement('script');
            jsNode.src = "";

On a partial template that may or may not be included within another template, we don't want to load jQuery twice. The above script checks if jQuery exists, if not then it appends jQuery to the header.

Lets break it down. I live this part:  Javascript works within a framework of code blocks. They are encapsulated within parenthesis (). This serves for nothing else than to isolated it's workings from the code around it. a second set of parenthesis ()() makes the first set of parenthesis self-executing upon page load. This is completely independent of any <body onload=""> type of functionality and is executed inline upon being rendered by the browser. You can have multiple instances. They are executed linearly, synchronously (as far as I understand).

The reason for breaking up the word SCR'+'IPT is because this code is nested within an outer set of <script></script> tags and will end the outer set prematurely and break the page.

There, now I can always reference this post whenever I need to find this code snippet in the future.

That's a wrap.

CFHTTP directly to output

We store our files on S3. Before they are uploaded there we know the file content-type and store it in the database with the file name and other data.

Then out site links to the file. This was previously exposing the raw S3 link with key and signature to the user - which is ugly.

Yeah, ugly. But it was functional. Images would display in the browser and files of other content type would be prompted for download.

I wanted to change that but googling wasn't providing the answers.

I'm using the awesome Railo CFML server. There were some ACF examples, incomplete at best, which referenced "cfhttp.fileContent.toByteArray()", but that wasn't working in Railo.

After an hour of trial and lots of errors, I came up with this so-far-solidly working solution:

<cfhttp url="#variables.s3filelink#" getAsBinary="yes" method="get" />
<cfheader name="Content-Disposition" value="inline;filename=#variables.filename#" />
<cfcontent type="#variables.filetype#" reset="Yes" variable="cfhttp.filecontent">

This solution hides the external S3 link and preserves the view-in-browser for images and prompts for download on other file types.

Also, it was important for images that the user can right click on the displayed image and select to save the file locally to their machine. Using the cfheader line shown above and setting the filename=#variables.filename# attribute the original file name is prevserved when the user is prompted to download the file. I found without that setting the download file name was the name of my cfml template - which is bad because it looses the original file extision of .jpg or .pdf. Very important.

That's a wrap.