]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/js/swfupload/swfupload.js
WordPress 4.0-scripts
[autoinstalls/wordpress.git] / wp-includes / js / swfupload / swfupload.js
1 /**
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:
7  * http://www.opensource.org/licenses/mit-license.php
8  *
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
11  *
12  */
13
14
15 /* ******************* */
16 /* Constructor & Init  */
17 /* ******************* */
18 var SWFUpload;
19
20 if (SWFUpload == undefined) {
21         SWFUpload = function (settings) {
22                 this.initSWFUpload(settings);
23         };
24 }
25
26 SWFUpload.prototype.initSWFUpload = function (settings) {
27         try {
28                 this.customSettings = {};       // A container where developers can place their own settings associated with this instance.
29                 this.settings = settings;
30                 this.eventQueue = [];
31                 this.movieName = "SWFUpload_" + SWFUpload.movieCount++;
32                 this.movieElement = null;
33
34
35                 // Setup global control tracking
36                 SWFUpload.instances[this.movieName] = this;
37
38                 // Load the settings.  Load the Flash movie.
39                 this.initSettings();
40                 this.loadFlash();
41                 this.displayDebugInfo();
42         } catch (ex) {
43                 delete SWFUpload.instances[this.movieName];
44                 throw ex;
45         }
46 };
47
48 /* *************** */
49 /* Static Members  */
50 /* *************** */
51 SWFUpload.instances = {};
52 SWFUpload.movieCount = 0;
53 SWFUpload.version = "2.2.0 2009-03-25";
54 SWFUpload.QUEUE_ERROR = {
55         QUEUE_LIMIT_EXCEEDED                    : -100,
56         FILE_EXCEEDS_SIZE_LIMIT                 : -110,
57         ZERO_BYTE_FILE                                  : -120,
58         INVALID_FILETYPE                                : -130
59 };
60 SWFUpload.UPLOAD_ERROR = {
61         HTTP_ERROR                                              : -200,
62         MISSING_UPLOAD_URL                      : -210,
63         IO_ERROR                                                : -220,
64         SECURITY_ERROR                                  : -230,
65         UPLOAD_LIMIT_EXCEEDED                   : -240,
66         UPLOAD_FAILED                                   : -250,
67         SPECIFIED_FILE_ID_NOT_FOUND             : -260,
68         FILE_VALIDATION_FAILED                  : -270,
69         FILE_CANCELLED                                  : -280,
70         UPLOAD_STOPPED                                  : -290
71 };
72 SWFUpload.FILE_STATUS = {
73         QUEUED           : -1,
74         IN_PROGRESS      : -2,
75         ERROR            : -3,
76         COMPLETE         : -4,
77         CANCELLED        : -5
78 };
79 SWFUpload.BUTTON_ACTION = {
80         SELECT_FILE  : -100,
81         SELECT_FILES : -110,
82         START_UPLOAD : -120
83 };
84 SWFUpload.CURSOR = {
85         ARROW : -1,
86         HAND : -2
87 };
88 SWFUpload.WINDOW_MODE = {
89         WINDOW : "window",
90         TRANSPARENT : "transparent",
91         OPAQUE : "opaque"
92 };
93
94 // Private: takes a URL, determines if it is relative and converts to an absolute URL
95 // using the current site. Only processes the URL if it can, otherwise returns the URL untouched
96 SWFUpload.completeURL = function(url) {
97         if (typeof(url) !== "string" || url.match(/^https?:\/\//i) || url.match(/^\//)) {
98                 return url;
99         }
100         
101         var currentURL = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ":" + window.location.port : "");
102         
103         var indexSlash = window.location.pathname.lastIndexOf("/");
104         if (indexSlash <= 0) {
105                 path = "/";
106         } else {
107                 path = window.location.pathname.substr(0, indexSlash) + "/";
108         }
109         
110         return /*currentURL +*/ path + url;
111         
112 };
113
114
115 /* ******************** */
116 /* Instance Members  */
117 /* ******************** */
118
119 // Private: initSettings ensures that all the
120 // settings are set, getting a default value if one was not assigned.
121 SWFUpload.prototype.initSettings = function () {
122         this.ensureDefault = function (settingName, defaultValue) {
123                 this.settings[settingName] = (this.settings[settingName] == undefined) ? defaultValue : this.settings[settingName];
124         };
125         
126         // Upload backend settings
127         this.ensureDefault("upload_url", "");
128         this.ensureDefault("preserve_relative_urls", false);
129         this.ensureDefault("file_post_name", "Filedata");
130         this.ensureDefault("post_params", {});
131         this.ensureDefault("use_query_string", false);
132         this.ensureDefault("requeue_on_error", false);
133         this.ensureDefault("http_success", []);
134         this.ensureDefault("assume_success_timeout", 0);
135         
136         // File Settings
137         this.ensureDefault("file_types", "*.*");
138         this.ensureDefault("file_types_description", "All Files");
139         this.ensureDefault("file_size_limit", 0);       // Default zero means "unlimited"
140         this.ensureDefault("file_upload_limit", 0);
141         this.ensureDefault("file_queue_limit", 0);
142
143         // Flash Settings
144         this.ensureDefault("flash_url", "swfupload.swf");
145         this.ensureDefault("prevent_swf_caching", true);
146         
147         // Button Settings
148         this.ensureDefault("button_image_url", "");
149         this.ensureDefault("button_width", 1);
150         this.ensureDefault("button_height", 1);
151         this.ensureDefault("button_text", "");
152         this.ensureDefault("button_text_style", "color: #000000; font-size: 16pt;");
153         this.ensureDefault("button_text_top_padding", 0);
154         this.ensureDefault("button_text_left_padding", 0);
155         this.ensureDefault("button_action", SWFUpload.BUTTON_ACTION.SELECT_FILES);
156         this.ensureDefault("button_disabled", false);
157         this.ensureDefault("button_placeholder_id", "");
158         this.ensureDefault("button_placeholder", null);
159         this.ensureDefault("button_cursor", SWFUpload.CURSOR.ARROW);
160         this.ensureDefault("button_window_mode", SWFUpload.WINDOW_MODE.WINDOW);
161         
162         // Debug Settings
163         this.ensureDefault("debug", false);
164         this.settings.debug_enabled = this.settings.debug;      // Here to maintain v2 API
165         
166         // Event Handlers
167         this.settings.return_upload_start_handler = this.returnUploadStart;
168         this.ensureDefault("swfupload_loaded_handler", null);
169         this.ensureDefault("file_dialog_start_handler", null);
170         this.ensureDefault("file_queued_handler", null);
171         this.ensureDefault("file_queue_error_handler", null);
172         this.ensureDefault("file_dialog_complete_handler", null);
173         
174         this.ensureDefault("upload_start_handler", null);
175         this.ensureDefault("upload_progress_handler", null);
176         this.ensureDefault("upload_error_handler", null);
177         this.ensureDefault("upload_success_handler", null);
178         this.ensureDefault("upload_complete_handler", null);
179         
180         this.ensureDefault("debug_handler", this.debugMessage);
181
182         this.ensureDefault("custom_settings", {});
183
184         // Other settings
185         this.customSettings = this.settings.custom_settings;
186         
187         // Update the flash url if needed
188         if (!!this.settings.prevent_swf_caching) {
189                 this.settings.flash_url = this.settings.flash_url + (this.settings.flash_url.indexOf("?") < 0 ? "?" : "&") + "preventswfcaching=" + new Date().getTime();
190         }
191         
192         if (!this.settings.preserve_relative_urls) {
193                 //this.settings.flash_url = SWFUpload.completeURL(this.settings.flash_url);     // Don't need to do this one since flash doesn't look at it
194                 this.settings.upload_url = SWFUpload.completeURL(this.settings.upload_url);
195                 this.settings.button_image_url = SWFUpload.completeURL(this.settings.button_image_url);
196         }
197         
198         delete this.ensureDefault;
199 };
200
201 // Private: loadFlash replaces the button_placeholder element with the flash movie.
202 SWFUpload.prototype.loadFlash = function () {
203         var targetElement, tempParent;
204
205         // Make sure an element with the ID we are going to use doesn't already exist
206         if (document.getElementById(this.movieName) !== null) {
207                 throw "ID " + this.movieName + " is already in use. The Flash Object could not be added";
208         }
209
210         // Get the element where we will be placing the flash movie
211         targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;
212
213         if (targetElement == undefined) {
214                 throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
215         }
216
217         // Append the container and load the flash
218         tempParent = document.createElement("div");
219         tempParent.innerHTML = this.getFlashHTML();     // Using innerHTML is non-standard but the only sensible way to dynamically add Flash in IE (and maybe other browsers)
220         targetElement.parentNode.replaceChild(tempParent.firstChild, targetElement);
221
222         // Fix IE Flash/Form bug
223         if (window[this.movieName] == undefined) {
224                 window[this.movieName] = this.getMovieElement();
225         }
226         
227 };
228
229 // Private: getFlashHTML generates the object tag needed to embed the flash in to the document
230 SWFUpload.prototype.getFlashHTML = function () {
231         // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
232         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">',
233                                 '<param name="wmode" value="', this.settings.button_window_mode, '" />',
234                                 '<param name="movie" value="', this.settings.flash_url, '" />',
235                                 '<param name="quality" value="high" />',
236                                 '<param name="menu" value="false" />',
237                                 '<param name="allowScriptAccess" value="always" />',
238                                 '<param name="flashvars" value="' + this.getFlashVars() + '" />',
239                                 '</object>'].join("");
240 };
241
242 // Private: getFlashVars builds the parameter string that will be passed
243 // to flash in the flashvars param.
244 SWFUpload.prototype.getFlashVars = function () {
245         // Build a string from the post param object
246         var paramString = this.buildParamString();
247         var httpSuccessString = this.settings.http_success.join(",");
248         
249         // Build the parameter string
250         return ["movieName=", encodeURIComponent(this.movieName),
251                         "&amp;uploadURL=", encodeURIComponent(this.settings.upload_url),
252                         "&amp;useQueryString=", encodeURIComponent(this.settings.use_query_string),
253                         "&amp;requeueOnError=", encodeURIComponent(this.settings.requeue_on_error),
254                         "&amp;httpSuccess=", encodeURIComponent(httpSuccessString),
255                         "&amp;assumeSuccessTimeout=", encodeURIComponent(this.settings.assume_success_timeout),
256                         "&amp;params=", encodeURIComponent(paramString),
257                         "&amp;filePostName=", encodeURIComponent(this.settings.file_post_name),
258                         "&amp;fileTypes=", encodeURIComponent(this.settings.file_types),
259                         "&amp;fileTypesDescription=", encodeURIComponent(this.settings.file_types_description),
260                         "&amp;fileSizeLimit=", encodeURIComponent(this.settings.file_size_limit),
261                         "&amp;fileUploadLimit=", encodeURIComponent(this.settings.file_upload_limit),
262                         "&amp;fileQueueLimit=", encodeURIComponent(this.settings.file_queue_limit),
263                         "&amp;debugEnabled=", encodeURIComponent(this.settings.debug_enabled),
264                         "&amp;buttonImageURL=", encodeURIComponent(this.settings.button_image_url),
265                         "&amp;buttonWidth=", encodeURIComponent(this.settings.button_width),
266                         "&amp;buttonHeight=", encodeURIComponent(this.settings.button_height),
267                         "&amp;buttonText=", encodeURIComponent(this.settings.button_text),
268                         "&amp;buttonTextTopPadding=", encodeURIComponent(this.settings.button_text_top_padding),
269                         "&amp;buttonTextLeftPadding=", encodeURIComponent(this.settings.button_text_left_padding),
270                         "&amp;buttonTextStyle=", encodeURIComponent(this.settings.button_text_style),
271                         "&amp;buttonAction=", encodeURIComponent(this.settings.button_action),
272                         "&amp;buttonDisabled=", encodeURIComponent(this.settings.button_disabled),
273                         "&amp;buttonCursor=", encodeURIComponent(this.settings.button_cursor)
274                 ].join("");
275 };
276
277 // Public: getMovieElement retrieves the DOM reference to the Flash element added by SWFUpload
278 // The element is cached after the first lookup
279 SWFUpload.prototype.getMovieElement = function () {
280         if (this.movieElement == undefined) {
281                 this.movieElement = document.getElementById(this.movieName);
282         }
283
284         if (this.movieElement === null) {
285                 throw "Could not find Flash element";
286         }
287         
288         return this.movieElement;
289 };
290
291 // Private: buildParamString takes the name/value pairs in the post_params setting object
292 // and joins them up in to a string formatted "name=value&amp;name=value"
293 SWFUpload.prototype.buildParamString = function () {
294         var postParams = this.settings.post_params; 
295         var paramStringPairs = [];
296
297         if (typeof(postParams) === "object") {
298                 for (var name in postParams) {
299                         if (postParams.hasOwnProperty(name)) {
300                                 paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
301                         }
302                 }
303         }
304
305         return paramStringPairs.join("&amp;");
306 };
307
308 // Public: Used to remove a SWFUpload instance from the page. This method strives to remove
309 // all references to the SWF, and other objects so memory is properly freed.
310 // Returns true if everything was destroyed. Returns a false if a failure occurs leaving SWFUpload in an inconsistant state.
311 // Credits: Major improvements provided by steffen
312 SWFUpload.prototype.destroy = function () {
313         try {
314                 // Make sure Flash is done before we try to remove it
315                 this.cancelUpload(null, false);
316                 
317
318                 // Remove the SWFUpload DOM nodes
319                 var movieElement = null;
320                 movieElement = this.getMovieElement();
321                 
322                 if (movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
323                         // Loop through all the movie's properties and remove all function references (DOM/JS IE 6/7 memory leak workaround)
324                         for (var i in movieElement) {
325                                 try {
326                                         if (typeof(movieElement[i]) === "function") {
327                                                 movieElement[i] = null;
328                                         }
329                                 } catch (ex1) {}
330                         }
331
332                         // Remove the Movie Element from the page
333                         try {
334                                 movieElement.parentNode.removeChild(movieElement);
335                         } catch (ex) {}
336                 }
337                 
338                 // Remove IE form fix reference
339                 window[this.movieName] = null;
340
341                 // Destroy other references
342                 SWFUpload.instances[this.movieName] = null;
343                 delete SWFUpload.instances[this.movieName];
344
345                 this.movieElement = null;
346                 this.settings = null;
347                 this.customSettings = null;
348                 this.eventQueue = null;
349                 this.movieName = null;
350                 
351                 
352                 return true;
353         } catch (ex2) {
354                 return false;
355         }
356 };
357
358
359 // Public: displayDebugInfo prints out settings and configuration
360 // information about this SWFUpload instance.
361 // This function (and any references to it) can be deleted when placing
362 // SWFUpload in production.
363 SWFUpload.prototype.displayDebugInfo = function () {
364         this.debug(
365                 [
366                         "---SWFUpload Instance Info---\n",
367                         "Version: ", SWFUpload.version, "\n",
368                         "Movie Name: ", this.movieName, "\n",
369                         "Settings:\n",
370                         "\t", "upload_url:               ", this.settings.upload_url, "\n",
371                         "\t", "flash_url:                ", this.settings.flash_url, "\n",
372                         "\t", "use_query_string:         ", this.settings.use_query_string.toString(), "\n",
373                         "\t", "requeue_on_error:         ", this.settings.requeue_on_error.toString(), "\n",
374                         "\t", "http_success:             ", this.settings.http_success.join(", "), "\n",
375                         "\t", "assume_success_timeout:   ", this.settings.assume_success_timeout, "\n",
376                         "\t", "file_post_name:           ", this.settings.file_post_name, "\n",
377                         "\t", "post_params:              ", this.settings.post_params.toString(), "\n",
378                         "\t", "file_types:               ", this.settings.file_types, "\n",
379                         "\t", "file_types_description:   ", this.settings.file_types_description, "\n",
380                         "\t", "file_size_limit:          ", this.settings.file_size_limit, "\n",
381                         "\t", "file_upload_limit:        ", this.settings.file_upload_limit, "\n",
382                         "\t", "file_queue_limit:         ", this.settings.file_queue_limit, "\n",
383                         "\t", "debug:                    ", this.settings.debug.toString(), "\n",
384
385                         "\t", "prevent_swf_caching:      ", this.settings.prevent_swf_caching.toString(), "\n",
386
387                         "\t", "button_placeholder_id:    ", this.settings.button_placeholder_id.toString(), "\n",
388                         "\t", "button_placeholder:       ", (this.settings.button_placeholder ? "Set" : "Not Set"), "\n",
389                         "\t", "button_image_url:         ", this.settings.button_image_url.toString(), "\n",
390                         "\t", "button_width:             ", this.settings.button_width.toString(), "\n",
391                         "\t", "button_height:            ", this.settings.button_height.toString(), "\n",
392                         "\t", "button_text:              ", this.settings.button_text.toString(), "\n",
393                         "\t", "button_text_style:        ", this.settings.button_text_style.toString(), "\n",
394                         "\t", "button_text_top_padding:  ", this.settings.button_text_top_padding.toString(), "\n",
395                         "\t", "button_text_left_padding: ", this.settings.button_text_left_padding.toString(), "\n",
396                         "\t", "button_action:            ", this.settings.button_action.toString(), "\n",
397                         "\t", "button_disabled:          ", this.settings.button_disabled.toString(), "\n",
398
399                         "\t", "custom_settings:          ", this.settings.custom_settings.toString(), "\n",
400                         "Event Handlers:\n",
401                         "\t", "swfupload_loaded_handler assigned:  ", (typeof this.settings.swfupload_loaded_handler === "function").toString(), "\n",
402                         "\t", "file_dialog_start_handler assigned: ", (typeof this.settings.file_dialog_start_handler === "function").toString(), "\n",
403                         "\t", "file_queued_handler assigned:       ", (typeof this.settings.file_queued_handler === "function").toString(), "\n",
404                         "\t", "file_queue_error_handler assigned:  ", (typeof this.settings.file_queue_error_handler === "function").toString(), "\n",
405                         "\t", "upload_start_handler assigned:      ", (typeof this.settings.upload_start_handler === "function").toString(), "\n",
406                         "\t", "upload_progress_handler assigned:   ", (typeof this.settings.upload_progress_handler === "function").toString(), "\n",
407                         "\t", "upload_error_handler assigned:      ", (typeof this.settings.upload_error_handler === "function").toString(), "\n",
408                         "\t", "upload_success_handler assigned:    ", (typeof this.settings.upload_success_handler === "function").toString(), "\n",
409                         "\t", "upload_complete_handler assigned:   ", (typeof this.settings.upload_complete_handler === "function").toString(), "\n",
410                         "\t", "debug_handler assigned:             ", (typeof this.settings.debug_handler === "function").toString(), "\n"
411                 ].join("")
412         );
413 };
414
415 /* Note: addSetting and getSetting are no longer used by SWFUpload but are included
416         the maintain v2 API compatibility
417 */
418 // Public: (Deprecated) addSetting adds a setting value. If the value given is undefined or null then the default_value is used.
419 SWFUpload.prototype.addSetting = function (name, value, default_value) {
420     if (value == undefined) {
421         return (this.settings[name] = default_value);
422     } else {
423         return (this.settings[name] = value);
424         }
425 };
426
427 // Public: (Deprecated) getSetting gets a setting. Returns an empty string if the setting was not found.
428 SWFUpload.prototype.getSetting = function (name) {
429     if (this.settings[name] != undefined) {
430         return this.settings[name];
431         }
432
433     return "";
434 };
435
436
437
438 // Private: callFlash handles function calls made to the Flash element.
439 // Calls are made with a setTimeout for some functions to work around
440 // bugs in the ExternalInterface library.
441 SWFUpload.prototype.callFlash = function (functionName, argumentArray) {
442         argumentArray = argumentArray || [];
443         
444         var movieElement = this.getMovieElement();
445         var returnValue, returnString;
446
447         // Flash's method if calling ExternalInterface methods (code adapted from MooTools).
448         try {
449                 returnString = movieElement.CallFunction('<invoke name="' + functionName + '" returntype="javascript">' + __flash__argumentsToXML(argumentArray, 0) + '</invoke>');
450                 returnValue = eval(returnString);
451         } catch (ex) {
452                 throw "Call to " + functionName + " failed";
453         }
454         
455         // Unescape file post param values
456         if (returnValue != undefined && typeof returnValue.post === "object") {
457                 returnValue = this.unescapeFilePostParams(returnValue);
458         }
459
460         return returnValue;
461 };
462
463 /* *****************************
464         -- Flash control methods --
465         Your UI should use these
466         to operate SWFUpload
467    ***************************** */
468
469 // WARNING: this function does not work in Flash Player 10
470 // Public: selectFile causes a File Selection Dialog window to appear.  This
471 // dialog only allows 1 file to be selected.
472 SWFUpload.prototype.selectFile = function () {
473         this.callFlash("SelectFile");
474 };
475
476 // WARNING: this function does not work in Flash Player 10
477 // Public: selectFiles causes a File Selection Dialog window to appear/ This
478 // dialog allows the user to select any number of files
479 // Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
480 // If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
481 // for this bug.
482 SWFUpload.prototype.selectFiles = function () {
483         this.callFlash("SelectFiles");
484 };
485
486
487 // Public: startUpload starts uploading the first file in the queue unless
488 // the optional parameter 'fileID' specifies the ID 
489 SWFUpload.prototype.startUpload = function (fileID) {
490         this.callFlash("StartUpload", [fileID]);
491 };
492
493 // Public: cancelUpload cancels any queued file.  The fileID parameter may be the file ID or index.
494 // If you do not specify a fileID the current uploading file or first file in the queue is cancelled.
495 // If you do not want the uploadError event to trigger you can specify false for the triggerErrorEvent parameter.
496 SWFUpload.prototype.cancelUpload = function (fileID, triggerErrorEvent) {
497         if (triggerErrorEvent !== false) {
498                 triggerErrorEvent = true;
499         }
500         this.callFlash("CancelUpload", [fileID, triggerErrorEvent]);
501 };
502
503 // Public: stopUpload stops the current upload and requeues the file at the beginning of the queue.
504 // If nothing is currently uploading then nothing happens.
505 SWFUpload.prototype.stopUpload = function () {
506         this.callFlash("StopUpload");
507 };
508
509 /* ************************
510  * Settings methods
511  *   These methods change the SWFUpload settings.
512  *   SWFUpload settings should not be changed directly on the settings object
513  *   since many of the settings need to be passed to Flash in order to take
514  *   effect.
515  * *********************** */
516
517 // Public: getStats gets the file statistics object.
518 SWFUpload.prototype.getStats = function () {
519         return this.callFlash("GetStats");
520 };
521
522 // Public: setStats changes the SWFUpload statistics.  You shouldn't need to 
523 // change the statistics but you can.  Changing the statistics does not
524 // affect SWFUpload accept for the successful_uploads count which is used
525 // by the upload_limit setting to determine how many files the user may upload.
526 SWFUpload.prototype.setStats = function (statsObject) {
527         this.callFlash("SetStats", [statsObject]);
528 };
529
530 // Public: getFile retrieves a File object by ID or Index.  If the file is
531 // not found then 'null' is returned.
532 SWFUpload.prototype.getFile = function (fileID) {
533         if (typeof(fileID) === "number") {
534                 return this.callFlash("GetFileByIndex", [fileID]);
535         } else {
536                 return this.callFlash("GetFile", [fileID]);
537         }
538 };
539
540 // Public: addFileParam sets a name/value pair that will be posted with the
541 // file specified by the Files ID.  If the name already exists then the
542 // exiting value will be overwritten.
543 SWFUpload.prototype.addFileParam = function (fileID, name, value) {
544         return this.callFlash("AddFileParam", [fileID, name, value]);
545 };
546
547 // Public: removeFileParam removes a previously set (by addFileParam) name/value
548 // pair from the specified file.
549 SWFUpload.prototype.removeFileParam = function (fileID, name) {
550         this.callFlash("RemoveFileParam", [fileID, name]);
551 };
552
553 // Public: setUploadUrl changes the upload_url setting.
554 SWFUpload.prototype.setUploadURL = function (url) {
555         this.settings.upload_url = url.toString();
556         this.callFlash("SetUploadURL", [url]);
557 };
558
559 // Public: setPostParams changes the post_params setting
560 SWFUpload.prototype.setPostParams = function (paramsObject) {
561         this.settings.post_params = paramsObject;
562         this.callFlash("SetPostParams", [paramsObject]);
563 };
564
565 // Public: addPostParam adds post name/value pair.  Each name can have only one value.
566 SWFUpload.prototype.addPostParam = function (name, value) {
567         this.settings.post_params[name] = value;
568         this.callFlash("SetPostParams", [this.settings.post_params]);
569 };
570
571 // Public: removePostParam deletes post name/value pair.
572 SWFUpload.prototype.removePostParam = function (name) {
573         delete this.settings.post_params[name];
574         this.callFlash("SetPostParams", [this.settings.post_params]);
575 };
576
577 // Public: setFileTypes changes the file_types setting and the file_types_description setting
578 SWFUpload.prototype.setFileTypes = function (types, description) {
579         this.settings.file_types = types;
580         this.settings.file_types_description = description;
581         this.callFlash("SetFileTypes", [types, description]);
582 };
583
584 // Public: setFileSizeLimit changes the file_size_limit setting
585 SWFUpload.prototype.setFileSizeLimit = function (fileSizeLimit) {
586         this.settings.file_size_limit = fileSizeLimit;
587         this.callFlash("SetFileSizeLimit", [fileSizeLimit]);
588 };
589
590 // Public: setFileUploadLimit changes the file_upload_limit setting
591 SWFUpload.prototype.setFileUploadLimit = function (fileUploadLimit) {
592         this.settings.file_upload_limit = fileUploadLimit;
593         this.callFlash("SetFileUploadLimit", [fileUploadLimit]);
594 };
595
596 // Public: setFileQueueLimit changes the file_queue_limit setting
597 SWFUpload.prototype.setFileQueueLimit = function (fileQueueLimit) {
598         this.settings.file_queue_limit = fileQueueLimit;
599         this.callFlash("SetFileQueueLimit", [fileQueueLimit]);
600 };
601
602 // Public: setFilePostName changes the file_post_name setting
603 SWFUpload.prototype.setFilePostName = function (filePostName) {
604         this.settings.file_post_name = filePostName;
605         this.callFlash("SetFilePostName", [filePostName]);
606 };
607
608 // Public: setUseQueryString changes the use_query_string setting
609 SWFUpload.prototype.setUseQueryString = function (useQueryString) {
610         this.settings.use_query_string = useQueryString;
611         this.callFlash("SetUseQueryString", [useQueryString]);
612 };
613
614 // Public: setRequeueOnError changes the requeue_on_error setting
615 SWFUpload.prototype.setRequeueOnError = function (requeueOnError) {
616         this.settings.requeue_on_error = requeueOnError;
617         this.callFlash("SetRequeueOnError", [requeueOnError]);
618 };
619
620 // Public: setHTTPSuccess changes the http_success setting
621 SWFUpload.prototype.setHTTPSuccess = function (http_status_codes) {
622         if (typeof http_status_codes === "string") {
623                 http_status_codes = http_status_codes.replace(" ", "").split(",");
624         }
625         
626         this.settings.http_success = http_status_codes;
627         this.callFlash("SetHTTPSuccess", [http_status_codes]);
628 };
629
630 // Public: setHTTPSuccess changes the http_success setting
631 SWFUpload.prototype.setAssumeSuccessTimeout = function (timeout_seconds) {
632         this.settings.assume_success_timeout = timeout_seconds;
633         this.callFlash("SetAssumeSuccessTimeout", [timeout_seconds]);
634 };
635
636 // Public: setDebugEnabled changes the debug_enabled setting
637 SWFUpload.prototype.setDebugEnabled = function (debugEnabled) {
638         this.settings.debug_enabled = debugEnabled;
639         this.callFlash("SetDebugEnabled", [debugEnabled]);
640 };
641
642 // Public: setButtonImageURL loads a button image sprite
643 SWFUpload.prototype.setButtonImageURL = function (buttonImageURL) {
644         if (buttonImageURL == undefined) {
645                 buttonImageURL = "";
646         }
647         
648         this.settings.button_image_url = buttonImageURL;
649         this.callFlash("SetButtonImageURL", [buttonImageURL]);
650 };
651
652 // Public: setButtonDimensions resizes the Flash Movie and button
653 SWFUpload.prototype.setButtonDimensions = function (width, height) {
654         this.settings.button_width = width;
655         this.settings.button_height = height;
656         
657         var movie = this.getMovieElement();
658         if (movie != undefined) {
659                 movie.style.width = width + "px";
660                 movie.style.height = height + "px";
661         }
662         
663         this.callFlash("SetButtonDimensions", [width, height]);
664 };
665 // Public: setButtonText Changes the text overlaid on the button
666 SWFUpload.prototype.setButtonText = function (html) {
667         this.settings.button_text = html;
668         this.callFlash("SetButtonText", [html]);
669 };
670 // Public: setButtonTextPadding changes the top and left padding of the text overlay
671 SWFUpload.prototype.setButtonTextPadding = function (left, top) {
672         this.settings.button_text_top_padding = top;
673         this.settings.button_text_left_padding = left;
674         this.callFlash("SetButtonTextPadding", [left, top]);
675 };
676
677 // Public: setButtonTextStyle changes the CSS used to style the HTML/Text overlaid on the button
678 SWFUpload.prototype.setButtonTextStyle = function (css) {
679         this.settings.button_text_style = css;
680         this.callFlash("SetButtonTextStyle", [css]);
681 };
682 // Public: setButtonDisabled disables/enables the button
683 SWFUpload.prototype.setButtonDisabled = function (isDisabled) {
684         this.settings.button_disabled = isDisabled;
685         this.callFlash("SetButtonDisabled", [isDisabled]);
686 };
687 // Public: setButtonAction sets the action that occurs when the button is clicked
688 SWFUpload.prototype.setButtonAction = function (buttonAction) {
689         this.settings.button_action = buttonAction;
690         this.callFlash("SetButtonAction", [buttonAction]);
691 };
692
693 // Public: setButtonCursor changes the mouse cursor displayed when hovering over the button
694 SWFUpload.prototype.setButtonCursor = function (cursor) {
695         this.settings.button_cursor = cursor;
696         this.callFlash("SetButtonCursor", [cursor]);
697 };
698
699 /* *******************************
700         Flash Event Interfaces
701         These functions are used by Flash to trigger the various
702         events.
703         
704         All these functions a Private.
705         
706         Because the ExternalInterface library is buggy the event calls
707         are added to a queue and the queue then executed by a setTimeout.
708         This ensures that events are executed in a determinate order and that
709         the ExternalInterface bugs are avoided.
710 ******************************* */
711
712 SWFUpload.prototype.queueEvent = function (handlerName, argumentArray) {
713         // Warning: Don't call this.debug inside here or you'll create an infinite loop
714         
715         if (argumentArray == undefined) {
716                 argumentArray = [];
717         } else if (!(argumentArray instanceof Array)) {
718                 argumentArray = [argumentArray];
719         }
720         
721         var self = this;
722         if (typeof this.settings[handlerName] === "function") {
723                 // Queue the event
724                 this.eventQueue.push(function () {
725                         this.settings[handlerName].apply(this, argumentArray);
726                 });
727                 
728                 // Execute the next queued event
729                 setTimeout(function () {
730                         self.executeNextEvent();
731                 }, 0);
732                 
733         } else if (this.settings[handlerName] !== null) {
734                 throw "Event handler " + handlerName + " is unknown or is not a function";
735         }
736 };
737
738 // Private: Causes the next event in the queue to be executed.  Since events are queued using a setTimeout
739 // we must queue them in order to garentee that they are executed in order.
740 SWFUpload.prototype.executeNextEvent = function () {
741         // Warning: Don't call this.debug inside here or you'll create an infinite loop
742
743         var  f = this.eventQueue ? this.eventQueue.shift() : null;
744         if (typeof(f) === "function") {
745                 f.apply(this);
746         }
747 };
748
749 // Private: unescapeFileParams is part of a workaround for a flash bug where objects passed through ExternalInterface cannot have
750 // properties that contain characters that are not valid for JavaScript identifiers. To work around this
751 // the Flash Component escapes the parameter names and we must unescape again before passing them along.
752 SWFUpload.prototype.unescapeFilePostParams = function (file) {
753         var reg = /[$]([0-9a-f]{4})/i;
754         var unescapedPost = {};
755         var uk;
756
757         if (file != undefined) {
758                 for (var k in file.post) {
759                         if (file.post.hasOwnProperty(k)) {
760                                 uk = k;
761                                 var match;
762                                 while ((match = reg.exec(uk)) !== null) {
763                                         uk = uk.replace(match[0], String.fromCharCode(parseInt("0x" + match[1], 16)));
764                                 }
765                                 unescapedPost[uk] = file.post[k];
766                         }
767                 }
768
769                 file.post = unescapedPost;
770         }
771
772         return file;
773 };
774
775 // Private: Called by Flash to see if JS can call in to Flash (test if External Interface is working)
776 SWFUpload.prototype.testExternalInterface = function () {
777         try {
778                 return this.callFlash("TestExternalInterface");
779         } catch (ex) {
780                 return false;
781         }
782 };
783
784 // Private: This event is called by Flash when it has finished loading. Don't modify this.
785 // Use the swfupload_loaded_handler event setting to execute custom code when SWFUpload has loaded.
786 SWFUpload.prototype.flashReady = function () {
787         // Check that the movie element is loaded correctly with its ExternalInterface methods defined
788         var movieElement = this.getMovieElement();
789
790         if (!movieElement) {
791                 this.debug("Flash called back ready but the flash movie can't be found.");
792                 return;
793         }
794
795         this.cleanUp(movieElement);
796         
797         this.queueEvent("swfupload_loaded_handler");
798 };
799
800 // Private: removes Flash added fuctions to the DOM node to prevent memory leaks in IE.
801 // This function is called by Flash each time the ExternalInterface functions are created.
802 SWFUpload.prototype.cleanUp = function (movieElement) {
803         // Pro-actively unhook all the Flash functions
804         try {
805                 if (this.movieElement && typeof(movieElement.CallFunction) === "unknown") { // We only want to do this in IE
806                         this.debug("Removing Flash functions hooks (this should only run in IE and should prevent memory leaks)");
807                         for (var key in movieElement) {
808                                 try {
809                                         if (typeof(movieElement[key]) === "function") {
810                                                 movieElement[key] = null;
811                                         }
812                                 } catch (ex) {
813                                 }
814                         }
815                 }
816         } catch (ex1) {
817         
818         }
819
820         // Fix Flashes own cleanup code so if the SWFMovie was removed from the page
821         // it doesn't display errors.
822         window["__flash__removeCallback"] = function (instance, name) {
823                 try {
824                         if (instance) {
825                                 instance[name] = null;
826                         }
827                 } catch (flashEx) {
828                 
829                 }
830         };
831
832 };
833
834
835 /* This is a chance to do something before the browse window opens */
836 SWFUpload.prototype.fileDialogStart = function () {
837         this.queueEvent("file_dialog_start_handler");
838 };
839
840
841 /* Called when a file is successfully added to the queue. */
842 SWFUpload.prototype.fileQueued = function (file) {
843         file = this.unescapeFilePostParams(file);
844         this.queueEvent("file_queued_handler", file);
845 };
846
847
848 /* Handle errors that occur when an attempt to queue a file fails. */
849 SWFUpload.prototype.fileQueueError = function (file, errorCode, message) {
850         file = this.unescapeFilePostParams(file);
851         this.queueEvent("file_queue_error_handler", [file, errorCode, message]);
852 };
853
854 /* Called after the file dialog has closed and the selected files have been queued.
855         You could call startUpload here if you want the queued files to begin uploading immediately. */
856 SWFUpload.prototype.fileDialogComplete = function (numFilesSelected, numFilesQueued, numFilesInQueue) {
857         this.queueEvent("file_dialog_complete_handler", [numFilesSelected, numFilesQueued, numFilesInQueue]);
858 };
859
860 SWFUpload.prototype.uploadStart = function (file) {
861         file = this.unescapeFilePostParams(file);
862         this.queueEvent("return_upload_start_handler", file);
863 };
864
865 SWFUpload.prototype.returnUploadStart = function (file) {
866         var returnValue;
867         if (typeof this.settings.upload_start_handler === "function") {
868                 file = this.unescapeFilePostParams(file);
869                 returnValue = this.settings.upload_start_handler.call(this, file);
870         } else if (this.settings.upload_start_handler != undefined) {
871                 throw "upload_start_handler must be a function";
872         }
873
874         // Convert undefined to true so if nothing is returned from the upload_start_handler it is
875         // interpretted as 'true'.
876         if (returnValue === undefined) {
877                 returnValue = true;
878         }
879         
880         returnValue = !!returnValue;
881         
882         this.callFlash("ReturnUploadStart", [returnValue]);
883 };
884
885
886
887 SWFUpload.prototype.uploadProgress = function (file, bytesComplete, bytesTotal) {
888         file = this.unescapeFilePostParams(file);
889         this.queueEvent("upload_progress_handler", [file, bytesComplete, bytesTotal]);
890 };
891
892 SWFUpload.prototype.uploadError = function (file, errorCode, message) {
893         file = this.unescapeFilePostParams(file);
894         this.queueEvent("upload_error_handler", [file, errorCode, message]);
895 };
896
897 SWFUpload.prototype.uploadSuccess = function (file, serverData, responseReceived) {
898         file = this.unescapeFilePostParams(file);
899         this.queueEvent("upload_success_handler", [file, serverData, responseReceived]);
900 };
901
902 SWFUpload.prototype.uploadComplete = function (file) {
903         file = this.unescapeFilePostParams(file);
904         this.queueEvent("upload_complete_handler", file);
905 };
906
907 /* Called by SWFUpload JavaScript and Flash functions when debug is enabled. By default it writes messages to the
908    internal debug console.  You can override this event and have messages written where you want. */
909 SWFUpload.prototype.debug = function (message) {
910         this.queueEvent("debug_handler", message);
911 };
912
913
914 /* **********************************
915         Debug Console
916         The debug console is a self contained, in page location
917         for debug message to be sent.  The Debug Console adds
918         itself to the body if necessary.
919
920         The console is automatically scrolled as messages appear.
921         
922         If you are using your own debug handler or when you deploy to production and
923         have debug disabled you can remove these functions to reduce the file size
924         and complexity.
925 ********************************** */
926    
927 // Private: debugMessage is the default debug_handler.  If you want to print debug messages
928 // call the debug() function.  When overriding the function your own function should
929 // check to see if the debug setting is true before outputting debug information.
930 SWFUpload.prototype.debugMessage = function (message) {
931         if (this.settings.debug) {
932                 var exceptionMessage, exceptionValues = [];
933
934                 // Check for an exception object and print it nicely
935                 if (typeof message === "object" && typeof message.name === "string" && typeof message.message === "string") {
936                         for (var key in message) {
937                                 if (message.hasOwnProperty(key)) {
938                                         exceptionValues.push(key + ": " + message[key]);
939                                 }
940                         }
941                         exceptionMessage = exceptionValues.join("\n") || "";
942                         exceptionValues = exceptionMessage.split("\n");
943                         exceptionMessage = "EXCEPTION: " + exceptionValues.join("\nEXCEPTION: ");
944                         SWFUpload.Console.writeLine(exceptionMessage);
945                 } else {
946                         SWFUpload.Console.writeLine(message);
947                 }
948         }
949 };
950
951 SWFUpload.Console = {};
952 SWFUpload.Console.writeLine = function (message) {
953         var console, documentForm;
954
955         try {
956                 console = document.getElementById("SWFUpload_Console");
957
958                 if (!console) {
959                         documentForm = document.createElement("form");
960                         document.getElementsByTagName("body")[0].appendChild(documentForm);
961
962                         console = document.createElement("textarea");
963                         console.id = "SWFUpload_Console";
964                         console.style.fontFamily = "monospace";
965                         console.setAttribute("wrap", "off");
966                         console.wrap = "off";
967                         console.style.overflow = "auto";
968                         console.style.width = "700px";
969                         console.style.height = "350px";
970                         console.style.margin = "5px";
971                         documentForm.appendChild(console);
972                 }
973
974                 console.value += message + "\n";
975
976                 console.scrollTop = console.scrollHeight - console.clientHeight;
977         } catch (ex) {
978                 alert("Exception: " + ex.name + " Message: " + ex.message);
979         }
980 };