ultimix
swfupload.js
Go to the documentation of this file.
1 
15 /* ******************* */
16 /* Constructor & Init */
17 /* ******************* */
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 };