Make WordPress Core

Ticket #6979: swfupload.1.diff

File swfupload.1.diff, 66.9 KB (added by filosofo, 16 years ago)
  • wp-includes/js/swfupload/swfupload.js

     
    11/**
    2  * SWFUpload v2.0 by Jacob Roberts, Nov 2007, http://www.swfupload.org, http://linebyline.blogspot.com
    3  * -------- -------- -------- -------- -------- -------- -------- --------
    4  * SWFUpload is (c) 2006 Lars Huring and Mammon Media and is released under the MIT License:
     2 * SWFUpload: http://www.swfupload.org, http://swfupload.googlecode.com
     3 *
     4 * mmSWFUpload 1.0: Flash upload dialog - http://profandesign.se/swfupload/,  http://www.vinterwebb.se/
     5 *
     6 * SWFUpload is (c) 2006-2007 Lars Huring, Olov Nilzén and Mammon Media and is released under the MIT License:
    57 * http://www.opensource.org/licenses/mit-license.php
    68 *
    7  * See Changelog.txt for version history
     9 * SWFUpload 2 is (c) 2007-2008 Jake Roberts and is released under the MIT License:
     10 * http://www.opensource.org/licenses/mit-license.php
    811 *
    9  * Development Notes:
    10  *  * This version of SWFUpload requires Flash Player 9.0.28 and should autodetect the correct flash version.
    11  *  * In Linux Flash Player 9 setting the post file variable name does not work. It is always set to "Filedata".
    12  *  * There is a lot of repeated code that could be refactored to single functions.  Feel free.
    13  *  * It's dangerous to do "circular calls" between Flash and JavaScript. I've taken steps to try to work around issues
    14  *     by having the event calls pipe through setTimeout.  However you should still avoid calling in to Flash from
    15  *     within the event handler methods.  Especially the "startUpload" event since it cannot use the setTimeout hack.
    1612 */
    1713
    1814
    19 /* *********** */
    20 /* Constructor */
    21 /* *********** */
     15/* ******************* */
     16/* Constructor & Init  */
     17/* ******************* */
    2218
    23 var SWFUpload = function (init_settings) {
    24         this.initSWFUpload(init_settings);
     19var SWFUpload = function (settings) {
     20        this.initSWFUpload(settings);
    2521};
    2622
    27 SWFUpload.prototype.initSWFUpload = function (init_settings) {
    28         // Remove background flicker in IE (read this: http://misterpixel.blogspot.com/2006/09/forensic-analysis-of-ie6.html)
    29         // This doesn't have anything to do with SWFUpload but can help your UI behave better in IE.
     23SWFUpload.prototype.initSWFUpload = function (settings) {
    3024        try {
    31                 document.execCommand('BackgroundImageCache', false, true);
    32         } catch (ex1) {
    33         }
    34 
    35 
    36         try {
    3725                this.customSettings = {};       // A container where developers can place their own settings associated with this instance.
    38                 this.settings = {};
     26                this.settings = settings;
    3927                this.eventQueue = [];
    4028                this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
    4129                this.movieElement = null;
     
    4432                SWFUpload.instances[this.movieName] = this;
    4533
    4634                // Load the settings.  Load the Flash movie.
    47                 this.initSettings(init_settings);
     35                this.initSettings();
    4836                this.loadFlash();
    49 
    5037                this.displayDebugInfo();
    51 
    52         } catch (ex2) {
    53                 this.debug(ex2);
     38        } catch (ex) {
     39                delete SWFUpload.instances[this.movieName];
     40                throw ex;
    5441        }
    55 }
     42};
    5643
    5744/* *************** */
    58 /* Static thingies */
     45/* Static Members */
    5946/* *************** */
    6047SWFUpload.instances = {};
    6148SWFUpload.movieCount = 0;
     49SWFUpload.version = "2.2.0 Alpha";
    6250SWFUpload.QUEUE_ERROR = {
    6351        QUEUE_LIMIT_EXCEEDED                    : -100,
    6452        FILE_EXCEEDS_SIZE_LIMIT                 : -110,
     
    8472        COMPLETE         : -4,
    8573        CANCELLED        : -5
    8674};
     75SWFUpload.BUTTON_ACTION = {
     76        SELECT_FILE  : -100,
     77        SELECT_FILES : -110,
     78        START_UPLOAD : -120
     79};
    8780
     81/* ******************** */
     82/* Instance Members  */
     83/* ******************** */
    8884
    89 /* ***************** */
    90 /* Instance Thingies */
    91 /* ***************** */
    92 // init is a private method that ensures that all the object settings are set, getting a default value if one was not assigned.
    93 
    94 SWFUpload.prototype.initSettings = function (init_settings) {
     85// Private: initSettings ensures that all the
     86// settings are set, getting a default value if one was not assigned.
     87SWFUpload.prototype.initSettings = function () {
     88        this.ensureDefault = function (settingName, defaultValue) {
     89                this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
     90        };
     91       
    9592        // Upload backend settings
    96         this.addSetting("upload_url",                           init_settings.upload_url,                               "");
    97         this.addSetting("file_post_name",                       init_settings.file_post_name,                   "Filedata");
    98         this.addSetting("post_params",                          init_settings.post_params,                              {});
    99 
     93        this.ensureDefault("upload_url", "");
     94        this.ensureDefault("file_post_name", "Filedata");
     95        this.ensureDefault("post_params", {});
     96        this.ensureDefault("use_query_string", false);
     97        this.ensureDefault("requeue_on_error", false);
     98       
    10099        // File Settings
    101         this.addSetting("file_types",                           init_settings.file_types,                               "*.*");
    102         this.addSetting("file_types_description",       init_settings.file_types_description,   "All Files");
    103         this.addSetting("file_size_limit",                      init_settings.file_size_limit,                  "1024");
    104         this.addSetting("file_upload_limit",            init_settings.file_upload_limit,                "0");
    105         this.addSetting("file_queue_limit",                     init_settings.file_queue_limit,                 "0");
     100        this.ensureDefault("file_types", "*.*");
     101        this.ensureDefault("file_types_description", "All Files");
     102        this.ensureDefault("file_size_limit", 0);       // Default zero means "unlimited"
     103        this.ensureDefault("file_upload_limit", 0);
     104        this.ensureDefault("file_queue_limit", 0);
    106105
    107106        // Flash Settings
    108         this.addSetting("flash_url",                            init_settings.flash_url,                                "swfupload.swf");
    109         this.addSetting("flash_width",                          init_settings.flash_width,                              "1px");
    110         this.addSetting("flash_height",                         init_settings.flash_height,                             "1px");
    111         this.addSetting("flash_color",                          init_settings.flash_color,                              "#FFFFFF");
    112 
     107        this.ensureDefault("flash_url", "swfupload.swf");
     108        this.ensureDefault("prevent_swf_caching", true);
     109       
     110        // Button Settings
     111        this.ensureDefault("button_image_url", "");
     112        this.ensureDefault("button_width", 1);
     113        this.ensureDefault("button_height", 1);
     114        this.ensureDefault("button_text", "");
     115        this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
     116        this.ensureDefault("button_text_top_padding", 0);
     117        this.ensureDefault("button_text_left_padding", 0);
     118        this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
     119        this.ensureDefault("button_disabled", false);
     120        this.ensureDefault("button_placeholder_id", null);
     121       
    113122        // Debug Settings
    114         this.addSetting("debug_enabled", init_settings.debug,  false);
    115 
     123        this.ensureDefault("debug", false);
     124        this.settings.debug_enabled = this.settings.debug;      // Here to maintain v2 API
     125       
    116126        // Event Handlers
    117         this.flashReady_handler         = SWFUpload.flashReady; // This is a non-overrideable event handler
    118         this.swfUploadLoaded_handler    = this.retrieveSetting(init_settings.swfupload_loaded_handler,      SWFUpload.swfUploadLoaded);
     127        this.settings.return_upload_start_handler = this.returnUploadStart;
     128        this.ensureDefault("swfupload_loaded_handler", null);
     129        this.ensureDefault("file_dialog_start_handler", null);
     130        this.ensureDefault("file_queued_handler", null);
     131        this.ensureDefault("file_queue_error_handler", null);
     132        this.ensureDefault("file_dialog_complete_handler", null);
    119133       
    120         this.fileDialogStart_handler    = this.retrieveSetting(init_settings.file_dialog_start_handler,         SWFUpload.fileDialogStart);
    121         this.fileQueued_handler                 = this.retrieveSetting(init_settings.file_queued_handler,                       SWFUpload.fileQueued);
    122         this.fileQueueError_handler             = this.retrieveSetting(init_settings.file_queue_error_handler,          SWFUpload.fileQueueError);
    123         this.fileDialogComplete_handler = this.retrieveSetting(init_settings.file_dialog_complete_handler,      SWFUpload.fileDialogComplete);
     134        this.ensureDefault("upload_start_handler", null);
     135        this.ensureDefault("upload_progress_handler", null);
     136        this.ensureDefault("upload_error_handler", null);
     137        this.ensureDefault("upload_success_handler", null);
     138        this.ensureDefault("upload_complete_handler", null);
    124139       
    125         this.uploadStart_handler                = this.retrieveSetting(init_settings.upload_start_handler,                      SWFUpload.uploadStart);
    126         this.uploadProgress_handler             = this.retrieveSetting(init_settings.upload_progress_handler,           SWFUpload.uploadProgress);
    127         this.uploadError_handler                = this.retrieveSetting(init_settings.upload_error_handler,                      SWFUpload.uploadError);
    128         this.uploadSuccess_handler              = this.retrieveSetting(init_settings.upload_success_handler,            SWFUpload.uploadSuccess);
    129         this.uploadComplete_handler             = this.retrieveSetting(init_settings.upload_complete_handler,           SWFUpload.uploadComplete);
     140        this.ensureDefault("debug_handler", this.debugMessage);
    130141
    131         this.debug_handler                              = this.retrieveSetting(init_settings.debug_handler,                                     SWFUpload.debug);
     142        this.ensureDefault("custom_settings", {});
    132143
    133144        // Other settings
    134         this.customSettings = this.retrieveSetting(init_settings.custom_settings, {});
     145        this.customSettings = this.settings.custom_settings;
     146       
     147        // Update the flash url if needed
     148        if (this.settings.prevent_swf_caching) {
     149                this.settings.flash_url = this.settings.flash_url + "?swfuploadrnd=" + Math.floor(Math.random() * 999999999);
     150        }
     151       
     152        delete this.ensureDefault;
    135153};
    136154
    137 // loadFlash is a private method that generates the HTML tag for the Flash
    138 // It then adds the flash to the "target" or to the body and stores a
    139 // reference to the flash element in "movieElement".
    140155SWFUpload.prototype.loadFlash = function () {
    141         var html, target_element, container;
     156        if (this.settings.button_placeholder_id !== "") {
     157                this.replaceWithFlash();
     158        } else {
     159                this.appendFlash();
     160        }
     161};
    142162
     163// Private: appendFlash gets the HTML tag for the Flash
     164// It then appends the flash to the body
     165SWFUpload.prototype.appendFlash = function () {
     166        var targetElement, container;
     167
    143168        // Make sure an element with the ID we are going to use doesn't already exist
    144169        if (document.getElementById(this.movieName) !== null) {
    145                 return false;
     170                throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
    146171        }
    147172
    148173        // Get the body tag where we will be adding the flash movie
    149         try {
    150                 target_element = document.getElementsByTagName("body")[0];
    151                 if (typeof(target_element) === "undefined" || target_element === null) {
    152                         this.debug('Could not find the BODY element. SWFUpload failed to load.');
    153                         return false;
    154                 }
    155         } catch (ex) {
    156                 return false;
     174        targetElement = document.getElementsByTagName("body")[0];
     175
     176        if (targetElement == undefined) {
     177                throw "Could not find the 'body' element.";
    157178        }
    158179
    159180        // Append the container and load the flash
    160181        container = document.createElement("div");
    161         container.style.width = this.getSetting("flash_width");
    162         container.style.height = this.getSetting("flash_height");
     182        container.style.width = "1px";
     183        container.style.height = "1px";
     184        container.style.overflow = "hidden";
    163185
    164         target_element.appendChild(container);
     186        targetElement.appendChild(container);
    165187        container.innerHTML = this.getFlashHTML();      // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
    166188};
    167189
    168 // Generates the embed/object tags needed to embed the flash in to the document
    169 SWFUpload.prototype.getFlashHTML = function () {
    170         var html = "";
     190// Private: replaceWithFlash replaces the button_placeholder element with the flash movie.
     191SWFUpload.prototype.replaceWithFlash = function () {
     192        var targetElement, tempParent;
    171193
    172         // Create Mozilla Embed HTML
    173         if (navigator.plugins && navigator.mimeTypes && navigator.mimeTypes.length) {
    174                 // Build the basic embed html
    175                 html = '<embed type="application/x-shockwave-flash" src="' + this.getSetting("flash_url") + '" width="' + this.getSetting("flash_width") + '" height="' + this.getSetting("flash_height") + '"';
    176                 html += ' id="' + this.movieName + '" name="' + this.movieName + '" ';
    177                 html += 'bgcolor="' + this.getSetting("flash_color") + '" quality="high" menu="false" flashvars="';
     194        // Make sure an element with the ID we are going to use doesn't already exist
     195        if (document.getElementById(this.movieName) !== null) {
     196                throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
     197        }
    178198
    179                 html += this.getFlashVars();
     199        // Get the element where we will be placing the flash movie
     200        targetElement = document.getElementById(this.settings.button_placeholder_id);
    180201
    181                 html += '" />';
     202        if (targetElement == undefined) {
     203                throw "Could not find the placeholder element.";
     204        }
    182205
    183                 // Create IE Object HTML
    184         } else {
     206        // Append the container and load the flash
     207        tempParent = document.createElement("div");
     208        tempParent.innerHTML = this.getFlashHTML();     // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
     209        targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);
    185210
    186                 // Build the basic Object tag
    187                 html = '<object id="' + this.movieName + '" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="' + this.getSetting("flash_width") + '" height="' + this.getSetting("flash_height") + '">';
    188                 html += '<param name="movie" value="' + this.getSetting("flash_url") + '">';
     211};
    189212
    190                 html += '<param name="bgcolor" value="' + this.getSetting("flash_color") + '" />';
    191                 html += '<param name="quality" value="high" />';
    192                 html += '<param name="menu" value="false" />';
    193 
    194                 html += '<param name="flashvars" value="' + this.getFlashVars() + '" />';
    195                 html += '</object>';
    196         }
    197 
    198         return html;
     213// Private: getFlashHTML generates the object tag needed to embed the flash in to the document
     214SWFUpload.prototype.getFlashHTML = function () {
     215        var transparent = this.settings.button_image_url === "" ? true : false;
     216       
     217        // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
     218        return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.settings.flash_url, '" width="', this.settings.button_width, '" height="', this.settings.button_height, '" class="swfupload">',
     219                                '<param name="wmode" value="', transparent ? "transparent" : "window", '" />',
     220                                '<param name="movie" value="', this.settings.flash_url, '" />',
     221                                '<param name="quality" value="high" />',
     222                                '<param name="menu" value="false" />',
     223                                '<param name="allowScriptAccess" value="always" />',
     224                                '<param name="flashvars" value="' + this.getFlashVars() + '" />',
     225                                '</object>'].join("");
    199226};
    200227
    201 // This private method builds the parameter string that will be passed
    202 // to flash.
     228// Private: getFlashVars builds the parameter string that will be passed
     229// to flash in the flashvars param.
    203230SWFUpload.prototype.getFlashVars = function () {
    204231        // Build a string from the post param object
    205         var param_string = this.buildParamString();
     232        var paramString = this.buildParamString();
    206233
    207234        // Build the parameter string
    208         var html = "";
    209         html += "movieName=" + encodeURIComponent(this.movieName);
    210         html += "&uploadURL=" + encodeURIComponent(this.getSetting("upload_url"));
    211         html += "&params=" + encodeURIComponent(param_string);
    212         html += "&filePostName=" + encodeURIComponent(this.getSetting("file_post_name"));
    213         html += "&fileTypes=" + encodeURIComponent(this.getSetting("file_types"));
    214         html += "&fileTypesDescription=" + encodeURIComponent(this.getSetting("file_types_description"));
    215         html += "&fileSizeLimit=" + encodeURIComponent(this.getSetting("file_size_limit"));
    216         html += "&fileUploadLimit=" + encodeURIComponent(this.getSetting("file_upload_limit"));
    217         html += "&fileQueueLimit=" + encodeURIComponent(this.getSetting("file_queue_limit"));
    218         html += "&debugEnabled=" + encodeURIComponent(this.getSetting("debug_enabled"));
    219 
    220         return html;
     235        return ["movieName=", encodeURIComponent(this.movieName),
     236                        "&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
     237                        "&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
     238                        "&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
     239                        "&amp;params=", encodeURIComponent(paramString),
     240                        "&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
     241                        "&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
     242                        "&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
     243                        "&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
     244                        "&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
     245                        "&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
     246                        "&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
     247                        "&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
     248                        "&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
     249                        "&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
     250                        "&amp;buttonText=", encodeURIComponent(this.settings.button_text),
     251                        "&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
     252                        "&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
     253                        "&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
     254                        "&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
     255                        "&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled)
     256                ].join("");
    221257};
    222258
     259// Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
     260// The element is cached after the first lookup
    223261SWFUpload.prototype.getMovieElement = function () {
    224         if (typeof(this.movieElement) === "undefined" || this.movieElement === null) {
     262        if (this.movieElement == undefined) {
    225263                this.movieElement = document.getElementById(this.movieName);
    226 
    227                 // Fix IEs "Flash can't callback when in a form" issue (http://www.extremefx.com.ar/blog/fixing-flash-external-interface-inside-form-on-internet-explorer)
    228                 // Removed because Revision 6 always adds the flash to the body (inside a containing div)
    229                 // If you insist on adding the Flash file inside a Form then in IE you have to make you wait until the DOM is ready
    230                 // and run this code to make the form's ID available from the window object so Flash and JavaScript can communicate.
    231                 //if (typeof(window[this.movieName]) === "undefined" || window[this.moveName] !== this.movieElement) {
    232                 //      window[this.movieName] = this.movieElement;
    233                 //}
    234264        }
    235265
     266        if (this.movieElement === null) {
     267                throw "Could not find Flash element";
     268        }
     269       
    236270        return this.movieElement;
    237271};
    238272
     273// Private: buildParamString takes the name/value pairs in the post_params setting object
     274// and joins them up in to a string formatted "name=value&amp;name=value"
    239275SWFUpload.prototype.buildParamString = function () {
    240         var post_params = this.getSetting("post_params");
    241         var param_string_pairs = [];
    242         var i, value, name;
     276        var postParams = this.settings.post_params;
     277        var paramStringPairs = [];
    243278
    244         // Retrieve the user defined parameters
    245         if (typeof(post_params) === "object") {
    246                 for (name in post_params) {
    247                         if (post_params.hasOwnProperty(name)) {
    248                                 if (typeof(post_params[name]) === "string") {
    249                                         param_string_pairs.push(encodeURIComponent(name) + "=" + encodeURIComponent(post_params[name]));
    250                                 }
     279        if (typeof(postParams) === "object") {
     280                for (var name in postParams) {
     281                        if (postParams.hasOwnProperty(name)) {
     282                                paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
    251283                        }
    252284                }
    253285        }
    254286
    255         return param_string_pairs.join("&");
     287        return paramStringPairs.join("&amp;");
    256288};
    257289
    258 // Saves a setting.      If the value given is undefined or null then the default_value is used.
    259 SWFUpload.prototype.addSetting = function (name, value, default_value) {
    260         if (typeof(value) === "undefined" || value === null) {
    261                 this.settings[name] = default_value;
    262         } else {
    263                 this.settings[name] = value;
    264         }
     290// Public: Used to remove a SWFUpload instance from the page. This method strives to remove
     291// all references to the SWF, and other objects so memory is properly freed.
     292// Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
     293SWFUpload.prototype.destroy = function () {
     294        try {
     295                // Make sure Flash is done before we try to remove it
     296                this.stopUpload();
     297               
     298                // Remove the SWFUpload DOM nodes
     299                var movieElement = null;
     300                try {
     301                        movieElement = this.getMovieElement();
     302                } catch (ex) {
     303                }
     304               
     305                if (movieElement != undefined && movieElement.parentNode != undefined && typeof movieElement.parentNode.removeChild === "function") {
     306                        var container = movieElement.parentNode;
     307                        if (container != undefined) {
     308                                container.removeChild(movieElement);
     309                                if (container.parentNode != undefined && typeof container.parentNode.removeChild === "function") {
     310                                        container.parentNode.removeChild(container);
     311                                }
     312                        }
     313                }
     314               
     315                // Destroy references
     316                SWFUpload.instances[this.movieName] = null;
     317                delete SWFUpload.instances[this.movieName];
    265318
    266         return this.settings[name];
    267 };
    268 
    269 // Gets a setting.      Returns empty string if not found.
    270 SWFUpload.prototype.getSetting = function (name) {
    271         if (typeof(this.settings[name]) === "undefined") {
    272                 return "";
    273         } else {
    274                 return this.settings[name];
     319                delete this.movieElement;
     320                delete this.settings;
     321                delete this.customSettings;
     322                delete this.eventQueue;
     323                delete this.movieName;
     324               
     325                delete window[this.movieName];
     326               
     327                return true;
     328        } catch (ex1) {
     329                return false;
    275330        }
    276331};
    277332
    278 // Gets a setting, if the setting is undefined then return the default value
    279 // This does not affect or use the interal setting object.
    280 SWFUpload.prototype.retrieveSetting = function (value, default_value) {
    281         if (typeof(value) === "undefined" || value === null) {
    282                 return default_value;
    283         } else {
    284                 return value;
    285         }
    286 };
    287 
    288 
    289 // It loops through all the settings and displays
    290 // them in the debug Console.
     333// Public: displayDebugInfo prints out settings and configuration
     334// information about this SWFUpload instance.
     335// This function (and any references to it) can be deleted when placing
     336// SWFUpload in production.
    291337SWFUpload.prototype.displayDebugInfo = function () {
    292         var key, debug_message = "";
     338        this.debug(
     339                [
     340                        "---SWFUpload Instance Info---\n",
     341                        "Version: ", SWFUpload.version, "\n",
     342                        "Movie Name: ", this.movieName, "\n",
     343                        "Settings:\n",
     344                        "\t", "upload_url:               ", this.settings.upload_url, "\n",
     345                        "\t", "flash_url:                ", this.settings.flash_url, "\n",
     346                        "\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
     347                        "\t", "file_post_name:           ", this.settings.file_post_name, "\n",
     348                        "\t", "post_params:              ", this.settings.post_params.toString(), "\n",
     349                        "\t", "file_types:               ", this.settings.file_types, "\n",
     350                        "\t", "file_types_description:   ", this.settings.file_types_description, "\n",
     351                        "\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
     352                        "\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
     353                        "\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
     354                        "\t", "debug:                    ", this.settings.debug.toString(), "\n",
    293355
    294         debug_message += "----- SWFUPLOAD SETTINGS     ----\nID: " + this.moveName + "\n";
     356                        "\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",
    295357
    296         debug_message += this.outputObject(this.settings);
     358                        "\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
     359                        "\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
     360                        "\t", "button_width:             ", this.settings.button_width.toString(), "\n",
     361                        "\t", "button_height:            ", this.settings.button_height.toString(), "\n",
     362                        "\t", "button_text:              ", this.settings.button_text.toString(), "\n",
     363                        "\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
     364                        "\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
     365                        "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
     366                        "\t", "button_action:            ", this.settings.button_action.toString(), "\n",
     367                        "\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",
    297368
    298         debug_message += "----- SWFUPLOAD SETTINGS END ----\n";
    299         debug_message += "\n";
     369                        "\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
     370                        "Event Handlers:\n",
     371                        "\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
     372                        "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
     373                        "\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
     374                        "\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
     375                        "\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
     376                        "\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
     377                        "\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
     378                        "\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
     379                        "\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
     380                        "\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
     381                ].join("")
     382        );
     383};
    300384
    301         this.debug(debug_message);
     385/* Note: addSetting and getSetting are no longer used by SWFUpload but are included
     386        the maintain v2 API compatibility
     387*/
     388// Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
     389SWFUpload.prototype.addSetting = function (name, value, default_value) {
     390    if (value == undefined) {
     391        return (this.settings[name] = default_value);
     392    } else {
     393        return (this.settings[name] = value);
     394        }
    302395};
    303 SWFUpload.prototype.outputObject = function (object, prefix) {
    304         var output = "", key;
    305396
    306         if (typeof(prefix) !== "string") {
    307                 prefix = "";
     397// Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
     398SWFUpload.prototype.getSetting = function (name) {
     399    if (this.settings[name] != undefined) {
     400        return this.settings[name];
    308401        }
    309         if (typeof(object) !== "object") {
    310                 return "";
    311         }
    312402
    313         for (key in object) {
    314                 if (object.hasOwnProperty(key)) {
    315                         if (typeof(object[key]) === "object") {
    316                                 output += (prefix + key + ": { \n" + this.outputObject(object[key], "\t" + prefix) + prefix + "}" + "\n");
    317                         } else {
    318                                 output += (prefix + key + ": " + object[key] + "\n");
    319                         }
     403    return "";
     404};
     405
     406
     407
     408// Private: callFlash handles function calls made to the Flash element.
     409// Calls are made with a setTimeout for some functions to work around
     410// bugs in the ExternalInterface library.
     411SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
     412        argumentArray = argumentArray || [];
     413       
     414        var movieElement = this.getMovieElement();
     415        var returnValue;
     416
     417        if (typeof movieElement[functionName] === "function") {
     418                // We have to go through all this if/else stuff because the Flash functions don't have apply() and only accept the exact number of arguments.
     419                if (argumentArray.length === 0) {
     420                        returnValue = movieElement[functionName]();
     421                } else if (argumentArray.length === 1) {
     422                        returnValue = movieElement[functionName](argumentArray[0]);
     423                } else if (argumentArray.length === 2) {
     424                        returnValue = movieElement[functionName](argumentArray[0], argumentArray[1]);
     425                } else if (argumentArray.length === 3) {
     426                        returnValue = movieElement[functionName](argumentArray[0], argumentArray[1], argumentArray[2]);
     427                } else {
     428                        throw "Too many arguments";
    320429                }
     430               
     431                // Unescape file post param values
     432                if (returnValue != undefined && typeof returnValue.post === "object") {
     433                        returnValue = this.unescapeFilePostParams(returnValue);
     434                }
     435               
     436                return returnValue;
     437        } else {
     438                throw "Invalid function name: " + functionName;
    321439        }
    322 
    323         return output;
    324440};
    325441
     442
    326443/* *****************************
    327444        -- Flash control methods --
    328445        Your UI should use these
    329446        to operate SWFUpload
    330447   ***************************** */
    331448
     449// Public: selectFile causes a File Selection Dialog window to appear.  This
     450// dialog only allows 1 file to be selected. WARNING: this function does not work in Flash Player 10
    332451SWFUpload.prototype.selectFile = function () {
    333         var movie_element = this.getMovieElement();
    334         if (movie_element !== null && typeof(movie_element.SelectFile) === "function") {
    335                 try {
    336                         movie_element.SelectFile();
    337                 }
    338                 catch (ex) {
    339                         this.debug("Could not call SelectFile: " + ex);
    340                 }
    341         } else {
    342                 this.debug("Could not find Flash element");
    343         }
    344 
     452        this.callFlash("SelectFile");
    345453};
    346454
     455// Public: selectFiles causes a File Selection Dialog window to appear/ This
     456// dialog allows the user to select any number of files
     457// Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
     458// If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
     459// for this bug.  WARNING: this function does not work in Flash Player 10
    347460SWFUpload.prototype.selectFiles = function () {
    348         var movie_element = this.getMovieElement();
    349         if (movie_element !== null && typeof(movie_element.SelectFiles) === "function") {
    350                 try {
    351                         movie_element.SelectFiles();
    352                 }
    353                 catch (ex) {
    354                         this.debug("Could not call SelectFiles: " + ex);
    355                 }
    356         } else {
    357                 this.debug("Could not find Flash element");
    358         }
    359 
     461        this.callFlash("SelectFiles");
    360462};
    361463
    362464
    363 /* Start the upload.  If a file_id is specified that file is uploaded. Otherwise the first
    364  * file in the queue is uploaded.  If no files are in the queue then nothing happens.
    365  * This call uses setTimeout since Flash will be calling back in to JavaScript
    366  */
    367 SWFUpload.prototype.startUpload = function (file_id) {
    368         var self = this;
    369         var movie_element = this.getMovieElement();
    370         if (movie_element !== null && typeof(movie_element.StartUpload) === "function") {
    371                 setTimeout(
    372                         function () {
    373                                 try {
    374                                         movie_element.StartUpload(file_id);
    375                                 }
    376                                 catch (ex) {
    377                                         self.debug("Could not call StartUpload: " + ex);
    378                                 }
    379                         }, 0
    380                 );
    381         } else {
    382                 this.debug("Could not find Flash element");
    383         }
    384 
     465// Public: startUpload starts uploading the first file in the queue unless
     466// the optional parameter 'fileID' specifies the ID
     467SWFUpload.prototype.startUpload = function (fileID) {
     468        this.callFlash("StartUpload", [fileID]);
    385469};
    386470
    387471/* Cancels a the file upload.  You must specify a file_id */
    388 SWFUpload.prototype.cancelUpload = function (file_id) {
    389         var movie_element = this.getMovieElement();
    390         if (movie_element !== null && typeof(movie_element.CancelUpload) === "function") {
    391                 try {
    392                         movie_element.CancelUpload(file_id);
    393                 }
    394                 catch (ex) {
    395                         this.debug("Could not call CancelUpload: " + ex);
    396                 }
    397         } else {
    398                 this.debug("Could not find Flash element");
    399         }
    400 
     472// Public: cancelUpload cancels any queued file.  The fileID parameter
     473// must be specified.
     474SWFUpload.prototype.cancelUpload = function (fileID) {
     475        this.callFlash("CancelUpload", [fileID]);
    401476};
    402477
    403 // Stops the current upload.  The file is re-queued.  If nothing is currently uploading then nothing happens.
     478// Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
     479// If nothing is currently uploading then nothing happens.
    404480SWFUpload.prototype.stopUpload = function () {
    405         var movie_element = this.getMovieElement();
    406         if (movie_element !== null && typeof(movie_element.StopUpload) === "function") {
    407                 try {
    408                         movie_element.StopUpload();
    409                 }
    410                 catch (ex) {
    411                         this.debug("Could not call StopUpload: " + ex);
    412                 }
    413         } else {
    414                 this.debug("Could not find Flash element");
    415         }
    416 
     481        this.callFlash("StopUpload");
    417482};
    418483
    419484/* ************************
    420485 * Settings methods
    421  *   These methods change the settings inside SWFUpload
    422  *   They shouldn't need to be called in a setTimeout since they
    423  *   should not call back from Flash to JavaScript (except perhaps in a Debug call)
    424  *   and some need to return data so setTimeout won't work.
    425  */
     486 *   These methods change the SWFUpload settings.
     487 *   SWFUpload settings should not be changed directly on the settings object
     488 *   since many of the settings need to be passed to Flash in order to take
     489 *   effect.
     490 * *********************** */
    426491
    427 /* Gets the file statistics object.      It looks like this (where n = number):
    428         {
    429                 files_queued: n,
    430                 complete_uploads: n,
    431                 upload_errors: n,
    432                 uploads_cancelled: n,
    433                 queue_errors: n
    434         }
    435 */
     492// Public: getStats gets the file statistics object.
    436493SWFUpload.prototype.getStats = function () {
    437         var movie_element = this.getMovieElement();
    438         if (movie_element !== null && typeof(movie_element.GetStats) === "function") {
    439                 try {
    440                         return movie_element.GetStats();
    441                 }
    442                 catch (ex) {
    443                         this.debug("Could not call GetStats");
    444                 }
    445         } else {
    446                 this.debug("Could not find Flash element");
    447         }
     494        return this.callFlash("GetStats");
    448495};
    449 SWFUpload.prototype.setStats = function (stats_object) {
    450         var movie_element = this.getMovieElement();
    451         if (movie_element !== null && typeof(movie_element.SetStats) === "function") {
    452                 try {
    453                         movie_element.SetStats(stats_object);
    454                 }
    455                 catch (ex) {
    456                         this.debug("Could not call SetStats");
    457                 }
    458         } else {
    459                 this.debug("Could not find Flash element");
    460         }
     496
     497// Public: setStats changes the SWFUpload statistics.  You shouldn't need to
     498// change the statistics but you can.  Changing the statistics does not
     499// affect SWFUpload accept for the successful_uploads count which is used
     500// by the upload_limit setting to determine how many files the user may upload.
     501SWFUpload.prototype.setStats = function (statsObject) {
     502        this.callFlash("SetStats", [statsObject]);
    461503};
    462504
    463 SWFUpload.prototype.setCredentials = function(name, password) {
    464         var movie_element = this.getMovieElement();
    465         if (movie_element !== null && typeof(movie_element.SetCredentials) === "function") {
    466                 try {
    467                         return movie_element.SetCredentials(name, password);
    468                 }
    469                 catch (ex) {
    470                         this.debug("Could not call SetCredentials");
    471                 }
     505// Public: getFile retrieves a File object by ID or Index.  If the file is
     506// not found then 'null' is returned.
     507SWFUpload.prototype.getFile = function (fileID) {
     508        if (typeof(fileID) === "number") {
     509                return this.callFlash("GetFileByIndex", [fileID]);
    472510        } else {
    473                 this.debug("Could not find Flash element");
     511                return this.callFlash("GetFile", [fileID]);
    474512        }
    475513};
    476514
    477 SWFUpload.prototype.getFile = function (file_id) {
    478         var movie_element = this.getMovieElement();
    479                         if (typeof(file_id) === "number") {
    480                                 if (movie_element !== null && typeof(movie_element.GetFileByIndex) === "function") {
    481                                         try {
    482                                                 return movie_element.GetFileByIndex(file_id);
    483                                         }
    484                                         catch (ex) {
    485                                                 this.debug("Could not call GetFileByIndex");
    486                                         }
    487                                 } else {
    488                                         this.debug("Could not find Flash element");
    489                                 }
    490                         } else {
    491                                 if (movie_element !== null && typeof(movie_element.GetFile) === "function") {
    492                                         try {
    493                                                 return movie_element.GetFile(file_id);
    494                                         }
    495                                         catch (ex) {
    496                                                 this.debug("Could not call GetFile");
    497                                         }
    498                                 } else {
    499                                         this.debug("Could not find Flash element");
    500                                 }
    501                         }
     515// Public: addFileParam sets a name/value pair that will be posted with the
     516// file specified by the Files ID.  If the name already exists then the
     517// exiting value will be overwritten.
     518SWFUpload.prototype.addFileParam = function (fileID, name, value) {
     519        return this.callFlash("AddFileParam", [fileID, name, value]);
    502520};
    503521
    504 SWFUpload.prototype.addFileParam = function (file_id, name, value) {
    505         var movie_element = this.getMovieElement();
    506         if (movie_element !== null && typeof(movie_element.AddFileParam) === "function") {
    507                 try {
    508                         return movie_element.AddFileParam(file_id, name, value);
    509                 }
    510                 catch (ex) {
    511                         this.debug("Could not call AddFileParam");
    512                 }
    513         } else {
    514                 this.debug("Could not find Flash element");
    515         }
     522// Public: removeFileParam removes a previously set (by addFileParam) name/value
     523// pair from the specified file.
     524SWFUpload.prototype.removeFileParam = function (fileID, name) {
     525        this.callFlash("RemoveFileParam", [fileID, name]);
    516526};
    517527
    518 SWFUpload.prototype.removeFileParam = function (file_id, name) {
    519         var movie_element = this.getMovieElement();
    520         if (movie_element !== null && typeof(movie_element.RemoveFileParam) === "function") {
    521                 try {
    522                         return movie_element.RemoveFileParam(file_id, name);
    523                 }
    524                 catch (ex) {
    525                         this.debug("Could not call AddFileParam");
    526                 }
    527         } else {
    528                 this.debug("Could not find Flash element");
    529         }
     528// Public: setUploadUrl changes the upload_url setting.
     529SWFUpload.prototype.setUploadURL = function (url) {
     530        this.settings.upload_url = url.toString();
     531        this.callFlash("SetUploadURL", [url]);
     532};
    530533
     534// Public: setPostParams changes the post_params setting
     535SWFUpload.prototype.setPostParams = function (paramsObject) {
     536        this.settings.post_params = paramsObject;
     537        this.callFlash("SetPostParams", [paramsObject]);
    531538};
    532539
    533 SWFUpload.prototype.setUploadURL = function (url) {
    534         var movie_element = this.getMovieElement();
    535         if (movie_element !== null && typeof(movie_element.SetUploadURL) === "function") {
    536                 try {
    537                         this.addSetting("upload_url", url);
    538                         movie_element.SetUploadURL(this.getSetting("upload_url"));
    539                 }
    540                 catch (ex) {
    541                         this.debug("Could not call SetUploadURL");
    542                 }
    543         } else {
    544                 this.debug("Could not find Flash element in setUploadURL");
    545         }
     540// Public: addPostParam adds post name/value pair.  Each name can have only one value.
     541SWFUpload.prototype.addPostParam = function (name, value) {
     542        this.settings.post_params[name] = value;
     543        this.callFlash("SetPostParams", [this.settings.post_params]);
    546544};
    547545
    548 SWFUpload.prototype.setPostParams = function (param_object) {
    549         var movie_element = this.getMovieElement();
    550         if (movie_element !== null && typeof(movie_element.SetPostParams) === "function") {
    551                 try {
    552                         this.addSetting("post_params", param_object);
    553                         movie_element.SetPostParams(this.getSetting("post_params"));
    554                 }
    555                 catch (ex) {
    556                         this.debug("Could not call SetPostParams");
    557                 }
    558         } else {
    559                 this.debug("Could not find Flash element in SetPostParams");
    560         }
     546// Public: removePostParam deletes post name/value pair.
     547SWFUpload.prototype.removePostParam = function (name) {
     548        delete this.settings.post_params[name];
     549        this.callFlash("SetPostParams", [this.settings.post_params]);
    561550};
    562551
     552// Public: setFileTypes changes the file_types setting and the file_types_description setting
    563553SWFUpload.prototype.setFileTypes = function (types, description) {
    564         var movie_element = this.getMovieElement();
    565         if (movie_element !== null && typeof(movie_element.SetFileTypes) === "function") {
    566                 try {
    567                         this.addSetting("file_types", types);
    568                         this.addSetting("file_types_description", description);
    569                         movie_element.SetFileTypes(this.getSetting("file_types"), this.getSetting("file_types_description"));
    570                 }
    571                 catch (ex) {
    572                         this.debug("Could not call SetFileTypes");
    573                 }
    574         } else {
    575                 this.debug("Could not find Flash element in SetFileTypes");
    576         }
     554        this.settings.file_types = types;
     555        this.settings.file_types_description = description;
     556        this.callFlash("SetFileTypes", [types, description]);
    577557};
    578558
    579 SWFUpload.prototype.setFileSizeLimit = function (file_size_limit) {
    580         var movie_element = this.getMovieElement();
    581         if (movie_element !== null && typeof(movie_element.SetFileSizeLimit) === "function") {
    582                 try {
    583                         this.addSetting("file_size_limit", file_size_limit);
    584                         movie_element.SetFileSizeLimit(this.getSetting("file_size_limit"));
    585                 }
    586                 catch (ex) {
    587                         this.debug("Could not call SetFileSizeLimit");
    588                 }
    589         } else {
    590                 this.debug("Could not find Flash element in SetFileSizeLimit");
    591         }
     559// Public: setFileSizeLimit changes the file_size_limit setting
     560SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
     561        this.settings.file_size_limit = fileSizeLimit;
     562        this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
    592563};
    593564
    594 SWFUpload.prototype.setFileUploadLimit = function (file_upload_limit) {
    595         var movie_element = this.getMovieElement();
    596         if (movie_element !== null && typeof(movie_element.SetFileUploadLimit) === "function") {
    597                 try {
    598                         this.addSetting("file_upload_limit", file_upload_limit);
    599                         movie_element.SetFileUploadLimit(this.getSetting("file_upload_limit"));
    600                 }
    601                 catch (ex) {
    602                         this.debug("Could not call SetFileUploadLimit");
    603                 }
    604         } else {
    605                 this.debug("Could not find Flash element in SetFileUploadLimit");
    606         }
     565// Public: setFileUploadLimit changes the file_upload_limit setting
     566SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
     567        this.settings.file_upload_limit = fileUploadLimit;
     568        this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
    607569};
    608570
    609 SWFUpload.prototype.setFileQueueLimit = function (file_queue_limit) {
    610         var movie_element = this.getMovieElement();
    611         if (movie_element !== null && typeof(movie_element.SetFileQueueLimit) === "function") {
    612                 try {
    613                         this.addSetting("file_queue_limit", file_queue_limit);
    614                         movie_element.SetFileQueueLimit(this.getSetting("file_queue_limit"));
    615                 }
    616                 catch (ex) {
    617                         this.debug("Could not call SetFileQueueLimit");
    618                 }
    619         } else {
    620                 this.debug("Could not find Flash element in SetFileQueueLimit");
    621         }
     571// Public: setFileQueueLimit changes the file_queue_limit setting
     572SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
     573        this.settings.file_queue_limit = fileQueueLimit;
     574        this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
    622575};
    623576
    624 SWFUpload.prototype.setFilePostName = function (file_post_name) {
    625         var movie_element = this.getMovieElement();
    626         if (movie_element !== null && typeof(movie_element.SetFilePostName) === "function") {
    627                 try {
    628                         this.addSetting("file_post_name", file_post_name);
    629                         movie_element.SetFilePostName(this.getSetting("file_post_name"));
    630                 }
    631                 catch (ex) {
    632                         this.debug("Could not call SetFilePostName");
    633                 }
    634         } else {
    635                 this.debug("Could not find Flash element in SetFilePostName");
    636         }
     577// Public: setFilePostName changes the file_post_name setting
     578SWFUpload.prototype.setFilePostName = function (filePostName) {
     579        this.settings.file_post_name = filePostName;
     580        this.callFlash("SetFilePostName", [filePostName]);
    637581};
    638582
    639 SWFUpload.prototype.setDebugEnabled = function (debug_enabled) {
    640         var movie_element = this.getMovieElement();
    641         if (movie_element !== null && typeof(movie_element.SetDebugEnabled) === "function") {
    642                 try {
    643                         this.addSetting("debug_enabled", debug_enabled);
    644                         movie_element.SetDebugEnabled(this.getSetting("debug_enabled"));
    645                 }
    646                 catch (ex) {
    647                         this.debug("Could not call SetDebugEnabled");
    648                 }
    649         } else {
    650                 this.debug("Could not find Flash element in SetDebugEnabled");
    651         }
     583// Public: setUseQueryString changes the use_query_string setting
     584SWFUpload.prototype.setUseQueryString = function (useQueryString) {
     585        this.settings.use_query_string = useQueryString;
     586        this.callFlash("SetUseQueryString", [useQueryString]);
    652587};
    653588
    654 /* *******************************
    655         Internal Event Callers
    656         Don't override these! These event callers ensure that your custom event handlers
    657         are called safely and in order.
    658 ******************************* */
    659 
    660 /* This is the callback method that the Flash movie will call when it has been loaded and is ready to go.
    661    Calling this or showUI() "manually" will bypass the Flash Detection built in to SWFUpload.
    662    Use a ui_function setting if you want to control the UI loading after the flash has loaded.
    663 */
    664 SWFUpload.prototype.flashReady = function () {
    665         // Check that the movie element is loaded correctly with its ExternalInterface methods defined
    666         var movie_element = this.getMovieElement();
    667         if (movie_element === null || typeof(movie_element.StartUpload) !== "function") {
    668                 this.debug("ExternalInterface methods failed to initialize.");
    669                 return;
    670         }
    671        
    672         var self = this;
    673         if (typeof(self.flashReady_handler) === "function") {
    674                 this.eventQueue[this.eventQueue.length] = function() { self.flashReady_handler(); };
    675                 setTimeout(function () { self.executeNextEvent();}, 0);
    676         } else {
    677                 this.debug("flashReady_handler event not defined");
    678         }
     589// Public: setRequeueOnError changes the requeue_on_error setting
     590SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
     591        this.settings.requeue_on_error = requeueOnError;
     592        this.callFlash("SetRequeueOnError", [requeueOnError]);
    679593};
    680594
    681 /*
    682         Event Queue.  Rather can call events directly from Flash they events are
    683         are placed in a queue and then executed.  This ensures that each event is
    684         executed in the order it was called which is not guarenteed when calling
    685         setTimeout.  Out of order events was especially problematic in Safari.
    686 */
    687 SWFUpload.prototype.executeNextEvent = function () {
    688         var  f = this.eventQueue.shift();
    689         if (typeof(f) === "function") {
    690                 f();
    691         }
    692 }
    693 
    694 /* This is a chance to do something before the browse window opens */
    695 SWFUpload.prototype.fileDialogStart = function () {
    696         var self = this;
    697         if (typeof(self.fileDialogStart_handler) === "function") {
    698                 this.eventQueue[this.eventQueue.length] = function() { self.fileDialogStart_handler(); };
    699                 setTimeout(function () { self.executeNextEvent();}, 0);
    700         } else {
    701                 this.debug("fileDialogStart event not defined");
    702         }
     595// Public: setDebugEnabled changes the debug_enabled setting
     596SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
     597        this.settings.debug_enabled = debugEnabled;
     598        this.callFlash("SetDebugEnabled", [debugEnabled]);
    703599};
    704600
    705 
    706 /* Called when a file is successfully added to the queue. */
    707 SWFUpload.prototype.fileQueued = function (file) {
    708         var self = this;
    709         if (typeof(self.fileQueued_handler) === "function") {
    710                 this.eventQueue[this.eventQueue.length] = function() { self.fileQueued_handler(file); };
    711                 setTimeout(function () { self.executeNextEvent();}, 0);
    712         } else {
    713                 this.debug("fileQueued event not defined");
     601// Public: setButtonImageURL loads a button image sprite
     602SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
     603        if (buttonImageURL == undefined) {
     604                buttonImageURL = "";
    714605        }
     606       
     607        this.settings.button_image_url = buttonImageURL;
     608        this.callFlash("SetButtonImageURL", [buttonImageURL]);
    715609};
    716610
    717 
    718 /* Handle errors that occur when an attempt to queue a file fails. */
    719 SWFUpload.prototype.fileQueueError = function (file, error_code, message) {
    720         var self = this;
    721         if (typeof(self.fileQueueError_handler) === "function") {
    722                 this.eventQueue[this.eventQueue.length] = function() {  self.fileQueueError_handler(file, error_code, message); };
    723                 setTimeout(function () { self.executeNextEvent();}, 0);
    724         } else {
    725                 this.debug("fileQueueError event not defined");
     611// Public: setButtonDimensions resizes the Flash Movie and button
     612SWFUpload.prototype.setButtonDimensions = function (width, height) {
     613        this.settings.button_width = width;
     614        this.settings.button_height = height;
     615       
     616        var movie = this.getMovieElement();
     617        if (movie != undefined) {
     618                movie.style.width = width + "px";
     619                movie.style.height = height + "px";
    726620        }
     621       
     622        this.callFlash("SetButtonDimensions", [width, height]);
    727623};
     624// Public: setButtonText Changes the text overlaid on the button
     625SWFUpload.prototype.setButtonText = function (html) {
     626        this.settings.button_text = html;
     627        this.callFlash("SetButtonText", [html]);
     628};
     629// Public: setButtonTextPadding changes the top and left padding of the text overlay
     630SWFUpload.prototype.setButtonTextPadding = function (left, top) {
     631        this.settings.button_text_top_padding = top;
     632        this.settings.button_text_left_padding = left;
     633        this.callFlash("SetButtonTextPadding", [left, top]);
     634};
    728635
    729 /* Called after the file dialog has closed and the selected files have been queued.
    730         You could call startUpload here if you want the queued files to begin uploading immediately. */
    731 SWFUpload.prototype.fileDialogComplete = function (num_files_selected) {
    732         var self = this;
    733         if (typeof(self.fileDialogComplete_handler) === "function") {
    734                 this.eventQueue[this.eventQueue.length] = function() { self.fileDialogComplete_handler(num_files_selected); };
    735                 setTimeout(function () { self.executeNextEvent();}, 0);
    736         } else {
    737                 this.debug("fileDialogComplete event not defined");
    738         }
     636// Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
     637SWFUpload.prototype.setButtonTextStyle = function (css) {
     638        this.settings.button_text_style = css;
     639        this.callFlash("SetButtonTextStyle", [css]);
    739640};
     641// Public: setButtonDisabled disables/enables the button
     642SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
     643        this.settings.button_disabled = isDisabled;
     644        this.callFlash("SetButtonDisabled", [isDisabled]);
     645};
     646// Public: setButtonAction sets the action that occurs when the button is clicked
     647SWFUpload.prototype.setButtonAction = function (buttonAction) {
     648        this.settings.button_action = buttonAction;
     649        this.callFlash("SetButtonAction", [buttonAction]);
     650};
    740651
    741 /* Gets called when a file upload is about to be started.  Return true to continue the upload. Return false to stop the upload.
    742         If you return false then uploadError and uploadComplete are called (like normal).
     652/* *******************************
     653        Flash Event Interfaces
     654        These functions are used by Flash to trigger the various
     655        events.
    743656       
    744         This is a good place to do any file validation you need.
    745         */
    746 SWFUpload.prototype.uploadStart = function (file) {
     657        All these functions a Private.
     658       
     659        Because the ExternalInterface library is buggy the event calls
     660        are added to a queue and the queue then executed by a setTimeout.
     661        This ensures that events are executed in a determinate order and that
     662        the ExternalInterface bugs are avoided.
     663******************************* */
     664
     665SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
     666        // Warning: Don't call this.debug inside here or you'll create an infinite loop
     667       
     668        if (argumentArray == undefined) {
     669                argumentArray = [];
     670        } else if (!(argumentArray instanceof Array)) {
     671                argumentArray = [argumentArray];
     672        }
     673       
    747674        var self = this;
    748         if (typeof(self.fileDialogComplete_handler) === "function") {
    749                 this.eventQueue[this.eventQueue.length] = function() { self.returnUploadStart(self.uploadStart_handler(file)); };
    750                 setTimeout(function () { self.executeNextEvent();}, 0);
    751         } else {
    752                 this.debug("uploadStart event not defined");
     675        if (typeof this.settings[handlerName] === "function") {
     676                // Queue the event
     677                this.eventQueue.push(function () {
     678                        this.settings[handlerName].apply(this, argumentArray);
     679                });
     680               
     681                // Execute the next queued event
     682                setTimeout(function () {
     683                        self.executeNextEvent();
     684                }, 0);
     685               
     686        } else if (this.settings[handlerName] !== null) {
     687                throw "Event handler " + handlerName + " is unknown or is not a function";
    753688        }
    754689};
    755690
    756 /* Note: Internal use only.  This function returns the result of uploadStart to
    757         flash.  Since returning values in the normal way can result in Flash/JS circular
    758         call issues we split up the call in a Timeout.  This is transparent from the API
    759         point of view.
    760 */
    761 SWFUpload.prototype.returnUploadStart = function (return_value) {
    762         var movie_element = this.getMovieElement();
    763         if (movie_element !== null && typeof(movie_element.ReturnUploadStart) === "function") {
    764                 try {
    765                         movie_element.ReturnUploadStart(return_value);
    766                 }
    767                 catch (ex) {
    768                         this.debug("Could not call ReturnUploadStart");
    769                 }
    770         } else {
    771                 this.debug("Could not find Flash element in returnUploadStart");
     691// Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
     692// we must queue them in order to garentee that they are executed in order.
     693SWFUpload.prototype.executeNextEvent = function () {
     694        // Warning: Don't call this.debug inside here or you'll create an infinite loop
     695
     696        var  f = this.eventQueue ? this.eventQueue.shift() : null;
     697        if (typeof(f) === "function") {
     698                f.apply(this);
    772699        }
    773700};
    774701
     702// Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
     703// properties that contain characters that are not valid for JavaScript identifiers. To work around this
     704// the Flash Component escapes the parameter names and we must unescape again before passing them along.
     705SWFUpload.prototype.unescapeFilePostParams = function (file) {
     706        var reg = /[$]([0-9a-f]{4})/i;
     707        var unescapedPost = {};
     708        var uk;
    775709
     710        if (file != undefined) {
     711                for (var k in file.post) {
     712                        if (file.post.hasOwnProperty(k)) {
     713                                uk = k;
     714                                var match;
     715                                while ((match = reg.exec(uk)) !== null) {
     716                                        uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
     717                                }
     718                                unescapedPost[uk] = file.post[k];
     719                        }
     720                }
    776721
    777 /* Called during upload as the file progresses. Use this event to update your UI. */
    778 SWFUpload.prototype.uploadProgress = function (file, bytes_complete, bytes_total) {
    779         var self = this;
    780         if (typeof(self.uploadProgress_handler) === "function") {
    781                 this.eventQueue[this.eventQueue.length] = function() { self.uploadProgress_handler(file, bytes_complete, bytes_total); };
    782                 setTimeout(function () { self.executeNextEvent();}, 0);
    783         } else {
    784                 this.debug("uploadProgress event not defined");
     722                file.post = unescapedPost;
    785723        }
    786 };
    787724
    788 /* Called when an error occurs during an upload. Use error_code and the SWFUpload.UPLOAD_ERROR constants to determine
    789    which error occurred. The uploadComplete event is called after an error code indicating that the next file is
    790    ready for upload.  For files cancelled out of order the uploadComplete event will not be called. */
    791 SWFUpload.prototype.uploadError = function (file, error_code, message) {
    792         var self = this;
    793         if (typeof(this.uploadError_handler) === "function") {
    794                 this.eventQueue[this.eventQueue.length] = function() { self.uploadError_handler(file, error_code, message); };
    795                 setTimeout(function () { self.executeNextEvent();}, 0);
    796         } else {
    797                 this.debug("uploadError event not defined");
    798         }
     725        return file;
    799726};
    800727
    801 /* This gets called when a file finishes uploading and the server-side upload script has completed and returned a 200
    802 status code. Any text returned by the server is available in server_data.
    803 **NOTE: The upload script MUST return some text or the uploadSuccess and uploadComplete events will not fire and the
    804 upload will become 'stuck'. */
    805 SWFUpload.prototype.uploadSuccess = function (file, server_data) {
    806         var self = this;
    807         if (typeof(self.uploadSuccess_handler) === "function") {
    808                 this.eventQueue[this.eventQueue.length] = function() { self.uploadSuccess_handler(file, server_data); };
    809                 setTimeout(function () { self.executeNextEvent();}, 0);
    810         } else {
    811                 this.debug("uploadSuccess event not defined");
     728SWFUpload.prototype.flashReady = function () {
     729        // Check that the movie element is loaded correctly with its ExternalInterface methods defined
     730        var movieElement = this.getMovieElement();
     731        if (typeof movieElement.StartUpload !== "function") {
     732                throw "ExternalInterface methods failed to initialize.";
    812733        }
    813 };
    814734
    815 /* uploadComplete is called when the file is uploaded or an error occurred and SWFUpload is ready to make the next upload.
    816    If you want the next upload to start to automatically you can call startUpload() from this event. */
    817 SWFUpload.prototype.uploadComplete = function (file) {
    818         var self = this;
    819         if (typeof(self.uploadComplete_handler) === "function") {
    820                 this.eventQueue[this.eventQueue.length] = function() { self.uploadComplete_handler(file); };
    821                 setTimeout(function () { self.executeNextEvent();}, 0);
    822         } else {
    823                 this.debug("uploadComplete event not defined");
     735        // Fix IE Flash/Form bug
     736        if (window[this.movieName] == undefined) {
     737                window[this.movieName] = movieElement;
    824738        }
    825 };
    826 
    827 /* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
    828    internal debug console.  You can override this event and have messages written where you want. */
    829 SWFUpload.prototype.debug = function (message) {
    830         var self = this;
    831         if (typeof(self.debug_handler) === "function") {
    832                 this.eventQueue[this.eventQueue.length] = function() { self.debug_handler(message); };
    833                 setTimeout(function () { self.executeNextEvent();}, 0);
    834         } else {
    835                 this.eventQueue[this.eventQueue.length] = function() { self.debugMessage(message); };
    836                 setTimeout(function () { self.executeNextEvent();}, 0);
    837         }
    838 };
    839 
    840 
    841 /* **********************************
    842         Default Event Handlers.
    843         These event handlers are used by default if an overriding handler is
    844         not defined in the SWFUpload settings object.
    845739       
    846         JS Note: even though these are defined on the SWFUpload object (rather than the prototype) they
    847         are attached (read: copied) to a SWFUpload instance and 'this' is given the proper context.
    848    ********************************** */
    849 
    850 /* This is a special event handler that has no override in the settings.  Flash calls this when it has
    851    been loaded by the browser and is ready for interaction.  You should not override it.  If you need
    852    to do something with SWFUpload has loaded then use the swfupload_loaded_handler setting.
    853 */
    854 SWFUpload.flashReady = function () {
    855         try {
    856                 this.debug("Flash called back and is ready.");
    857 
    858                 if (typeof(this.swfUploadLoaded_handler) === "function") {
    859                         this.swfUploadLoaded_handler();
    860                 }
    861         } catch (ex) {
    862                 this.debug(ex);
    863         }
     740        this.queueEvent("swfupload_loaded_handler");
    864741};
    865742
    866 /* This is a chance to something immediately after SWFUpload has loaded.
    867    Like, hide the default/degraded upload form and display the SWFUpload form. */
    868 SWFUpload.swfUploadLoaded = function () {
    869 };
    870743
    871744/* This is a chance to do something before the browse window opens */
    872 SWFUpload.fileDialogStart = function () {
     745SWFUpload.prototype.fileDialogStart = function () {
     746        this.queueEvent("file_dialog_start_handler");
    873747};
    874748
    875749
    876750/* Called when a file is successfully added to the queue. */
    877 SWFUpload.fileQueued = function (file) {
     751SWFUpload.prototype.fileQueued = function (file) {
     752        file = this.unescapeFilePostParams(file);
     753        this.queueEvent("file_queued_handler", file);
    878754};
    879755
    880756
    881757/* Handle errors that occur when an attempt to queue a file fails. */
    882 SWFUpload.fileQueueError = function (file, error_code, message) {
    883         try {
    884                 switch (error_code) {
    885                 case SWFUpload.QUEUE_ERROR.FILE_EXCEEDS_SIZE_LIMIT:
    886                         this.debug("Error Code: File too big, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
    887                         break;
    888                 case SWFUpload.QUEUE_ERROR.ZERO_BYTE_FILE:
    889                         this.debug("Error Code: Zero Byte File, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
    890                         break;
    891                 case SWFUpload.QUEUE_ERROR.QUEUE_LIMIT_EXCEEDED:
    892                         this.debug("Error Code: Upload limit reached, File name: " + file.name + ", File size: " + file.size + ", Message: " + message);
    893                         break;
    894                 case SWFUpload.QUEUE_ERROR.INVALID_FILETYPE:
    895                         this.debug("Error Code: File extension is not allowed, Message: " + message);
    896                         break;
    897                 default:
    898                         this.debug("Error Code: Unhandled error occured. Errorcode: " + error_code);
    899                 }
    900         } catch (ex) {
    901                 this.debug(ex);
    902         }
     758SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
     759        file = this.unescapeFilePostParams(file);
     760        this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
    903761};
    904762
    905763/* Called after the file dialog has closed and the selected files have been queued.
    906764        You could call startUpload here if you want the queued files to begin uploading immediately. */
    907 SWFUpload.fileDialogComplete = function (num_files_selected) {
     765SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued) {
     766        this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued]);
    908767};
    909768
    910 /* Gets called when a file upload is about to be started.  Return true to continue the upload. Return false to stop the upload.
    911         If you return false then the uploadError callback is called and then uploadComplete (like normal).
     769SWFUpload.prototype.uploadStart = function (file) {
     770        file = this.unescapeFilePostParams(file);
     771        this.queueEvent("return_upload_start_handler", file);
     772};
     773
     774SWFUpload.prototype.returnUploadStart = function (file) {
     775        var returnValue;
     776        if (typeof this.settings.upload_start_handler === "function") {
     777                file = this.unescapeFilePostParams(file);
     778                returnValue = this.settings.upload_start_handler.call(this, file);
     779        } else if (this.settings.upload_start_handler != undefined) {
     780                throw "upload_start_handler must be a function";
     781        }
     782
     783        // Convert undefined to true so if nothing is returned from the upload_start_handler it is
     784        // interpretted as 'true'.
     785        if (returnValue === undefined) {
     786                returnValue = true;
     787        }
    912788       
    913         This is a good place to do any file validation you need.
     789        returnValue = !!returnValue;
    914790       
    915         This is the only function that cannot be called on a setTimeout because it must return a value to Flash.
    916         You SHOULD NOT make any calls in to Flash (e.i, changing settings, getting stats, etc).  Flash Player bugs prevent
    917         calls in to Flash from working reliably.
    918 */
    919 SWFUpload.uploadStart = function (file) {
    920         return true;
     791        this.callFlash("ReturnUploadStart", [returnValue]);
    921792};
    922793
    923 // Called during upload as the file progresses
    924 SWFUpload.uploadProgress = function (file, bytes_complete, bytes_total) {
    925         this.debug("File Progress: " + file.id + ", Bytes: " + bytes_complete + ". Total: " + bytes_total);
     794
     795
     796SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
     797        file = this.unescapeFilePostParams(file);
     798        this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
    926799};
    927800
    928 /* This gets called when a file finishes uploading and the upload script has completed and returned a 200 status code.  Any text returned by the
    929 server is available in server_data.      The upload script must return some text or uploadSuccess will not fire (neither will uploadComplete). */
    930 SWFUpload.uploadSuccess = function (file, server_data) {
    931         this.debug("Upload Success: " + file.id + ", Server: " + server_data);
     801SWFUpload.prototype.uploadError = function (file, errorCode, message) {
     802        file = this.unescapeFilePostParams(file);
     803        this.queueEvent("upload_error_handler", [file, errorCode, message]);
    932804};
    933805
    934 /* This is called last.  The file is uploaded or an error occurred and SWFUpload is ready to make the next upload.
    935         If you want to automatically start the next file just call startUpload from here.
    936 */
    937 SWFUpload.uploadComplete = function (file) {
    938         this.debug("Upload Complete: " + file.id);
     806SWFUpload.prototype.uploadSuccess = function (file, serverData) {
     807        file = this.unescapeFilePostParams(file);
     808        this.queueEvent("upload_success_handler", [file, serverData]);
    939809};
    940810
    941 // Called by SWFUpload JavaScript and Flash functions when debug is enabled.
    942 // Override this method in your settings to call your own debug message handler
    943 SWFUpload.debug = function (message) {
    944         if (this.getSetting("debug_enabled")) {
    945                 this.debugMessage(message);
    946         }
     811SWFUpload.prototype.uploadComplete = function (file) {
     812        file = this.unescapeFilePostParams(file);
     813        this.queueEvent("upload_complete_handler", file);
    947814};
    948815
    949 /* Called when an upload occurs during upload.  For HTTP errors 'message' will contain the HTTP STATUS CODE */
    950 SWFUpload.uploadError = function (file, errcode, msg) {
    951         try {
    952                 switch (errcode) {
    953                 case SWFUpload.UPLOAD_ERROR.SPECIFIED_FILE_ID_NOT_FOUND:
    954                         this.debug("Error Code: File ID specified for upload was not found, Message: " + msg);
    955                         break;
    956                 case SWFUpload.UPLOAD_ERROR.HTTP_ERROR:
    957                         this.debug("Error Code: HTTP Error, File name: " + file.name + ", Message: " + msg);
    958                         break;
    959                 case SWFUpload.UPLOAD_ERROR.MISSING_UPLOAD_URL:
    960                         this.debug("Error Code: No backend file, File name: " + file.name + ", Message: " + msg);
    961                         break;
    962                 case SWFUpload.UPLOAD_ERROR.IO_ERROR:
    963                         this.debug("Error Code: IO Error, File name: " + file.name + ", Message: " + msg);
    964                         break;
    965                 case SWFUpload.UPLOAD_ERROR.SECURITY_ERROR:
    966                         this.debug("Error Code: Security Error, File name: " + file.name + ", Message: " + msg);
    967                         break;
    968                 case SWFUpload.UPLOAD_ERROR.UPLOAD_LIMIT_EXCEEDED:
    969                         this.debug("Error Code: Upload limit reached, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
    970                         break;
    971                 case SWFUpload.UPLOAD_ERROR.UPLOAD_FAILED:
    972                         this.debug("Error Code: Upload Initialization exception, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
    973                         break;
    974                 case SWFUpload.UPLOAD_ERROR.FILE_VALIDATION_FAILED:
    975                         this.debug("Error Code: uploadStart callback returned false, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
    976                         break;
    977                 case SWFUpload.UPLOAD_ERROR.FILE_CANCELLED:
    978                         this.debug("Error Code: The file upload was cancelled, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
    979                         break;
    980                 case SWFUpload.UPLOAD_ERROR.UPLOAD_STOPPED:
    981                         this.debug("Error Code: The file upload was stopped, File name: " + file.name + ", File size: " + file.size + ", Message: " + msg);
    982                         break;
    983                 default:
    984                         this.debug("Error Code: Unhandled error occured. Errorcode: " + errcode);
    985                 }
    986         } catch (ex) {
    987                 this.debug(ex);
    988         }
     816/* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
     817   internal debug console.  You can override this event and have messages written where you want. */
     818SWFUpload.prototype.debug = function (message) {
     819        this.queueEvent("debug_handler", message);
    989820};
    990821
    991822
    992 
    993823/* **********************************
    994824        Debug Console
    995825        The debug console is a self contained, in page location
     
    998828
    999829        The console is automatically scrolled as messages appear.
    1000830       
    1001         You can override this console (to use FireBug's console for instance) by setting the debug event method to your own function
    1002         that handles the debug message
    1003    ********************************** */
     831        If you are using your own debug handler or when you deploy to production and
     832        have debug disabled you can remove these functions to reduce the file size
     833        and complexity.
     834********************************** */
     835   
     836// Private: debugMessage is the default debug_handler.  If you want to print debug messages
     837// call the debug() function.  When overriding the function your own function should
     838// check to see if the debug setting is true before outputting debug information.
    1004839SWFUpload.prototype.debugMessage = function (message) {
    1005         var exception_message, exception_values;
     840        if (this.settings.debug) {
     841                var exceptionMessage, exceptionValues = [];
    1006842
    1007         if (typeof(message) === "object" && typeof(message.name) === "string" && typeof(message.message) === "string") {
    1008                 exception_message = "";
    1009                 exception_values = [];
    1010                 for (var key in message) {
    1011                         exception_values.push(key + ": " + message[key]);
     843                // Check for an exception object and print it nicely
     844                if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
     845                        for (var key in message) {
     846                                if (message.hasOwnProperty(key)) {
     847                                        exceptionValues.push(key + ": " + message[key]);
     848                                }
     849                        }
     850                        exceptionMessage = exceptionValues.join("\n") || "";
     851                        exceptionValues = exceptionMessage.split("\n");
     852                        exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
     853                        SWFUpload.Console.writeLine(exceptionMessage);
     854                } else {
     855                        SWFUpload.Console.writeLine(message);
    1012856                }
    1013                 exception_message = exception_values.join("\n");
    1014                 exception_values = exception_message.split("\n");
    1015                 exception_message = "EXCEPTION: " + exception_values.join("\nEXCEPTION: ");
    1016                 SWFUpload.Console.writeLine(exception_message);
    1017         } else {
    1018                 SWFUpload.Console.writeLine(message);
    1019857        }
    1020858};
    1021859