I’ve been trying to get Uploadify to work inside my <a href=”http://github.com/jbasdf/uploader”Uploadify>uploader gem</a>. Things went pretty well until I combined that with the muck-contents gem and tried to do multiple file uploads from within a plugin I’d written for the TinyMCE editor. At that point things got ugly.
If you do much research on Uploadify you’ll find that it’s pretty common to hear someone complain that the OnComplete event does not fire. There’s a number of solutions that people have come up with, but mostly they sound like voodoo – Be sure to only return a ‘1’, make sure to use an id instead of a css class, etc. I tried everything I read about and finally got mad enough to debug the fla file.
If you are that desperate make sure to get FireFlash. It will let you write to the Firebug console from within your swf file. It’s very handy when you need to see what’s going on inside your Actionscript while running inside the browser. I created a debug version of the fla that output the events that the swf file was attempting to fire on the client. If you want to try it out in hopes it will help you debug download a zip file with the swf and fla.
OK so now to the point. What’s going wrong? Why doesn’t ‘onComplete’ fire? The answer is that Uploadify binds events to the DOM item you specify via jQuery(‘#my-upload-container’).uploadify(); These events include ‘uploadifySelect’, ‘uploadifyOpen’, ‘uploadifyProgress’, ‘uploadifyComplete’, etc. What I found is that the swf makes a call that looks like this:
ExternalInterface.call(['jQuery'+p(q('#'+param.uploadifyID)), p(list.join(','))].join('.trigger')); </pre>
That call does it's job - I can watch it make the ExternalInterface calls using FireFlash. The swf file attempts to trigger the events I listed above that have been bound to the element identified by id="my-upload-container". However, something goes wrong and somehow the 'my-upload-container' element loses it's bindings and nothing is fired. I found I can get things to work if I change 'jquery.uploadify.js' to bind the events above to the document instead of to the DOM element and then change the fla to do this:ExternalInterface.call(['jQuery(document)', p(list.join(','))].join('.trigger')); </pre>
That means that the events do fire however it's a hack so I kept digging (and swearing) and I bummed some help off Joel..... It turns out that the problem wasn't Flash. It wasn't uploadify. It wasn't jQuery although all of those components seemed like they might be the source of the problem. The problem as it turns out was TinyMCE. (As it turns out TinyMCE is the source of a lot of my problems but I still like it). In our specific case we've built a plugin to TinyMCE that let's you upload files to the server. (You can find it in the muck-contents gem). We use the TinyMCE scripts to build our popups which means using 'tiny_mce/tiny_mce_popup.js'. If you look in that code very deeply you will discover this code around line 320:if ((nv = t.editor.translate(h)) && nv != h) document.body.innerHTML = nv; </pre>
Notice that the way the popup handles translations is to replace the entire document with new html. If you use jQuery and your bindings have already been setup you realize this is very bad because everything you've setup using the standard methods just vanished. (This goes for all methods - click, bind, etc. They will all go away). ie This won't work:jQuery(document).ready(function(){ jQuery('#mce_uploader_upload').uploadify(mce_uploader_options); }); </pre>
The solution is simple and probably obvious to a seasoned TinyMCE dev (which I'm not). Put your code into a different initialization block:tinyMCEPopup.onInit.add(function(ed) { jQuery('#mce_uploader_upload').uploadify(mce_uploader_options); }); </pre>
As an aside I should mention that if you need help getting uploadify running in your Rails application there's already a couple of great tutorials. One by John Nunemaker and an update to get it working with Rails 3 updated version.