Introduction Have you ever had the need to implement a 3rd party JavaScript plugin, but your needs didn’t fit the model and usage defined by the API or documentation of the plugin? Recently I ran into this issue when I was trying to implement a web snapshot plugin into our site. To use their plugin, you had to include a script tag to the plugin on their server with an API key. The second part of the usage was to include a <script> tag around a function call wherever you wanted a snapshot to appear. The Problem When trying to use the service, the images did not display. I checked a couple of things and didn’t find anything wrong at first.. It wasn’t until I looked at the function that was called by the inline script did I find the issue – a call to the webservice, followed by a call to ‘document.write’ in its callback. The solution in which I was trying to implement the plugin happened to be in response to an AJAX call after the document had completely loaded. After the page has loaded, document.write does nothing. My first thought for a solution was to just cache the script from the service, and edit it do something like a return function or callback that I could use to edit the document from. However, I quickly discovered that there is no way to cache the script from the service, as it had a hash in the function where it would call the server. The hash was updated every few seconds/minutes, expiring old hashes. This meant that I wouldn’t be able to edit the script and upload a new version to my server, as the script would not work after a few minutes from originally getting the script from the service. Solution The solution eluded me until I realized that this was JavaScript I was dealing with. A language designed so that you could do just about anything to any library, function, or object… At this point, the solution was simple – take control of the document.write function. Using a buffer variable, and a simple function call, it is eerily simple to perform: //what would have been output to the document
var buffer = "";
//store a reference to the real document.write
var dw = document.write;
//redefine document.write to store to our buffer
document.write = function (str) {buffer += str;}
//execute the function containing calls to document.write
eval('{function encapsulated in <script></script> tags}');
//restore the original document.write function (just in case)
document.write = dw;
That’s it. Instead of using the script tags where I wanted to include a snapshot, I called a function passing in the URL to the page I wanted a snapshot of. After that last line of code, what would have been output to the document (or not in the case of the ajax call) was instead stored in buffer.
Conclusion
While the solution itself is simple, coming from a background much more footed in the .Net platform, I believe that this is a prime example of always keeping the language that you are working in in mind. While this may seem obvious at first, as I KNEW I was in JavaScript, I never thought of taking control of the document.write function because I am more accustomed to the .Net world. I can’t simply replace the functionality of Console.WriteLine.