PageRenderTime 78ms CodeModel.GetById 14ms app.highlight 55ms RepoModel.GetById 1ms app.codeStats 0ms

/source/Plug-in/swfupload/swfupload.js

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