function SWFU(id, settings) {
  var ref = {};
  ref.settings = {};

  ref.ajax_settings = {};
  ref.queue = {};
  ref.stats = {};
  ref.instance = {};
  ref.upload_stack_length = 0;
  ref.max_queue_size = 0;
  ref.upload_stack = {};
  ref.upload_stack_obj;
  ref.upload_button_obj;
  ref.upload_stack_size = 0;
  ref.wrapper_obj;
  ref.num_elements;
  ref.key_pressed;
  ref.message_wrapper_obj;
  ref.messages_timeout;

  /**
   * 
   */
  ref.init = function() {
    ref.settings = settings;
    ref.upload_button_obj = $('#edit-' + ref.settings.file_post_name);
    ref.instance = {name:settings.file_post_name};
    ref.ajax_settings = {
      type:"post",
      url:ref.settings.upload_url,
      data:{
        op:'init',
        filepath:ref.settings.post_params.filepath,
        instance:ref.toJson(ref.instance),
        node_settings:ref.settings.post_params.node_settings
      },
      success:function(result) {
        ref.ajaxResponse(result);
      }
    };

    ref.prepareSWFButton();
    // Get the instance data by an AJAX request in order to let other modules change the callbacks and elements for this instance (using hook_swfupload);
    $.ajax(ref.ajax_settings);

    // Firefox crashes here if swfupload.swf is loaded in a hidden collapsible fieldset.
    // I haven't excacly found out what causes this, but commenting 'duration' in Drupal.toggleFieldset solved this.
    // For now I'll overwrite the function.
    if ($.browser.mozilla && $('.collapsible.collapsed').size() > 0) {
      $.getScript(Drupal.settings.basePath + ref.settings.module_path + '/js/collapse.js');
    };
  };

  /**
   * Prepares the swfupload button.
   */
  ref.prepareSWFButton = function() {
    // Create a copy of the button to get it's dimensions.
    // If we'd use the original button, we could end up with dimensions equal to 0px when the button is inside a hidden fieldset.
    var tmp_button = ref.upload_button_obj.clone().css({'position':'absolute'}).prependTo('body');

    // Set the dimensions of the swf so it matches exactly the dimensions of the upload button
    // swfupload.swf will be placed exactly over the upload button
    ref.settings.button_width = (tmp_button.find('.left').width() + tmp_button.find('.center').width() + tmp_button.find('.right').width());
    ref.settings.button_height = tmp_button.find('.center').height();
    tmp_button.remove();

    // Add the other button settings to the settings object
    ref.settings.button_placeholder_id = ref.settings.file_post_name + '-swfwrapper';
    ref.settings.button_window_mode = SWFUpload.WINDOW_MODE.TRANSPARENT;
    ref.settings.button_cursor = SWFUpload.CURSOR.HAND;
  };

  /**
   * Creates a hidden input field which will contain a JSON formatted string containing all uploaded files
   */
  ref.createStackObj = function() {
    var upload_stack_value = settings.custom_settings.upload_stack_value;
    ref.max_queue_size = settings.custom_settings.max_queue_size;
    ref.upload_stack_obj = $('<input type="hidden" />').attr('name', ref.instance.name).val(upload_stack_value).prependTo(ref.upload_button_obj);
    ref.upload_stack = Drupal.parseJson(upload_stack_value);
    ref.upload_stack_length = ref.objectLength(ref.upload_stack);
  };

  /**
   * 
   */
  ref.newSWFUpload = function() {
    ref.swfu = new SWFUpload(ref.settings);
  };

  /**
   * 
   */
  ref.ajaxResponse = function(result) {
    var result = Drupal.parseJson(result);

    switch (result.op) {
      case 'init':
        ref.instance = result.instance;
        ref.num_elements = ref.objectLength(ref.instance.elements);
        $.each(result.instance.callbacks, function(setting, callback) {
          ref.settings[setting] = eval(callback);
        });
        ref.newSWFUpload();
        ref.settings.init_complete_handler(result);
        break;
    };
    ref.addEventHandlers(result.op);
  };

  /**
   * Custom function for when the initialization is complete
   * This event handler is defined in swfupload.module as an instance callback function 
   */
  ref.initComplete = function(result) {
    ref.createWrapper(result.instance.name);
    ref.createStackObj();
    ref.addStoredFiles();

    // Enable the upload button if the current stack is smaller than the allowed stack size.
    if (ref.settings.file_upload_limit && ref.settings.file_upload_limit !== 0 && (ref.upload_stack_length < ref.settings.file_upload_limit)) {
      ref.upload_button_obj.removeClass('disabled').css({opacity:1});
    }
    else {
      ref.upload_button_obj.addClass('disabled').css({opacity:0.4});
    };
  };

  /**
   * This will process all file elements stored in the upload stack.
   * The upload represents all files submitted in the upload form.
   * For all files in the stack, a file element will be added to the wrapper using ref.addFileItem().
   */
  ref.addStoredFiles = function() {
    for(var i in ref.upload_stack) {
      if (ref.upload_stack[i] == 0) {
        break;
      };
      ref.upload_stack[i].id = i;
      ref.upload_stack[i].fid = i;
      ref.upload_stack[i].extension = ref.getExtension(ref.upload_stack[i].filename);
      ref.addFileItem(ref.upload_stack[i]);

      // Adjust the bytes in the stack.
      ref.upload_stack_size += parseInt(ref.upload_stack[i].filesize);
    };
    ref.dragEnable();
  };

  /**
   * Places the wrapper markup above the upload button
   * Depending on what type isset by the instance, a table or a list element is created.
   */
  ref.createWrapper = function(name) {
    var use_header = false;
    var element;

    if (ref.num_elements > 1 && ref.instance.type == 'table') {
      // First we'll check if we need to create a header 
      for (var name in ref.instance.elements) {
        if (ref.instance.elements[name].title) {
           use_header = true;
        };
      };

      ref.wrapper_obj = $('<table />').attr({'id':'swfupload_file_wrapper-' + name, 'class':'swfupload'});
      if (use_header) {
        ref.wrapper_obj.append($('<thead />').append(ref.tableRow(true)));
      };
      ref.wrapper_obj.append($('<tbody />').append(ref.tableRow()));
      ref.upload_button_obj.before(ref.wrapper_obj);
    };
  };    

  /**
   * Creates or changes a tablerow
   * @param header Boolean Wheter or not the tablerow should contain th's. If sety to false, td's will be generated.
   * @param file Object A completed file object 
   *                         If this is not set, a row is created including the progressbar, which replaces the td's with contains_progressbar set to true.
   *                         If file is set, the progressbar will be replaced with the appropriate td's
   */
  ref.tableRow = function(header, file) {
    var counter = 0;
    var colspan = 0;
    var fid = (file) ? file.fid : 0;
    var progress_td_counter = 0;
    var element, colum, content, input, progress_td, value;

    var tr = (file) ? $('#' + file.fid) : $('<tr />');
    var wrapper = $('<div />').addClass('wrapper');
    var left_span = $('<div />').addClass('left').html('&nbsp;');
    var center_span = $('<div />').addClass('center');
    var right_span = $('<div />').addClass('right').html('&nbsp;');

    // A tablerow will be created containing all elements defined in ref.instance.elements.
    // If file is set, all elements will be skipped exept the ones with 'contains_progressbar'
    // If file isn't set, this tablerow will be hidden.
    for (var name in ref.instance.elements) {
      counter++;
      element = ref.instance.elements[name];

      if (file) {
        if(!element.contains_progressbar) {
          // The current td doesn't have to be replaced.
          // We only need to replace fid of the id and name of the input field
          tr.find('#edit-' + name + '_0').attr({'name':name +'_' + fid, 'id':'edit-' + name + '_' + fid});
          continue;
        };
      }
      else {
        if (!header && element.contains_progressbar) {
          if (!progress_td) {
            progress_td = $('<td />').addClass('progress').append($('<div />').addClass('sfwupload-list-progressbar').append($('<div />').addClass('sfwupload-list-progressbar-status')).append($('<div />').addClass('sfwupload-list-progressbar-glow'))).appendTo(tr);
          };
          progress_td_counter++;
          continue;
        };
      };

      column = $((header ? '<th />' : '<td />'));
      content = wrapper.clone().appendTo(column);
      input = $((element.type == 'textarea' ? '<textarea />' : '<input type="' + element.type + '" />')).attr({'name':name +'_' + fid, 'id':'edit-' + name + '_' + fid}).addClass('form-' + element.type);

      if (header) {
        // Keep track of colspans 
        if (colspan > 0) colspan--;
        if (element.colspan) {
          colspan = element.colspan;
        }
        else if (colspan !== 0) {
          continue;
        };

        // Add the colspan if set.
        if (element.colspan) {
          column.attr({'colSpan':element.colspan});
        };

        // Add a separator only if we're not dealing with the first or last column
        if (counter !== ref.num_elements && (counter + (colspan - 1) !== ref.num_elements) && element.add_separator) {
          content.append(left_span.clone()).append(right_span.clone());
        };

        content.append(center_span.clone().html((element.title ? element.title : '&nbsp;')));
      }
      else {
        // Create the content for this td
        // Depending on the type the appropriate input field is appended to store the values of this type
        switch (element.type) {
          case 'drag':
            content.append($('<div />').addClass('sfwupload-list-drag'));

            // Fix transparency for IE6
            if ($.cssPNGFix) {
              content.find('.sfwupload-list-drag').cssPNGFix();
            };
            break;
          case 'icon':
            content.append($('<div />').addClass('sfwupload-list-mime'));
            break;
          case 'cancel':
            content.append($('<div />').addClass('sfwupload-list-cancel'));
            break;
          case 'text':
            value = (file) ? (file[name] !== undefined) ? file[name] : ref.replaceMacros(element.default_value, file) : element.default_value;
            content.append($('<span />').html(value)).append(input.css({'display':'none'}).val(value));
            break;
          case 'textarea':
            value = (file) ? (file[name] !== undefined) ? file[name] : ref.replaceMacros(element.default_value, file) : element.default_value;
            content.append($('<span />').html((value !== '' ? value : '&nbsp;'))).append(input.css({'display':'none'}).val(value));
            break;
          case 'checkbox':
            value = (file[name] !== undefined) ? (typeof(file[name]) == 'string' ? (file[name] == '1') : file[name]) : element.default_value;
            // For IE we need to check the checkbox after the content has been added to the tr.
            // We'll temporarily store it's value in a classname
            content.append(input.addClass('checkbox ' + (value ? 'checked' : '')));
            break;
          default:
            break;
        };
      };

      // Add a classname if set.
      if (element.classname) {
        column.addClass(element.classname);
      };

      if (file && element.contains_progressbar) {
        column.insertBefore(tr.find('td.progress'));
      }
      else {
        tr.append(column);
      };
    };

    if (!header && !file) {
      // Hide the tablerow
      tr.addClass('hidden');
    };

    if (progress_td) {
      progress_td.attr({'colSpan':progress_td_counter});
    };

    // Update the checked value of all added checkboxes 
    tr.find('input.checkbox').each(function() {
      $(this).attr('checked', $(this).hasClass('checked'));
    });

    if (file) {
      tr.addClass('processed').find('td.progress').remove();
    }
    else {
      // Create borders
      var border = $(header ? '<th />' : '<td />').addClass('border').append($('<img />').attr({'src':Drupal.settings.basePath + ref.settings.module_path + '/images/spacer.gif'}).css({'width':'1px'}));
      tr.prepend(border.clone()).append(border);
      return tr;
    };
  };

  /**
   * A file has been selected. This function creates the markup referring to the new file object
   */
  ref.addFileItem = function(file) {
    // Create the markup for the new file by copying the hidden template 
    var new_file_obj = ref.wrapper_obj.find('.hidden').clone().attr({'id':file.id}).appendTo(ref.wrapper_obj);
    var input, value;

    // If it is a file earlier stored (a file in the upload_stack), remove it's progressbar.
    if (file.filestatus !== -1) {
      ref.tableRow(false, file);
    };

    // Replace macro's
    for (var name in ref.instance.elements) {
      input = new_file_obj.find('#edit-' + name + '_' + (file.fid || '0'));
      if (input.size() > 0) {
        value = (file[name] !== undefined) ? file[name] : ref.replaceMacros(ref.instance.elements[name].default_value, file);
        input.val(value);
        // If the inputfield is hidden, we're dealing with a string. 
        // Look if there is a span of which the text can be replaced
        if (input.css('display') == 'none') {
          input.parent().find('span').text(value);
        };
      };
    };
    // Add the extension to the mime icon
    new_file_obj.find('.sfwupload-list-mime').addClass(file.extension);

    // Fix transparency for IE6
    if ($.cssPNGFix) {
      new_file_obj.find('.sfwupload-list-mime').cssPNGFix();
    };

    new_file_obj.removeClass('hidden');
    ref.addEventHandlers((file.filestatus == -1 ? 'file_queued' : 'file_added'), file);
  };

  /**
   * Attaches all event handlers to the loaded markup
   */
  ref.addEventHandlers = function(op, file) {
    switch (op) {
      case 'flash_loaded':
        ref.upload_button_obj.find('.swfupload-wrapper .swfupload').mousedown(function() {
          $(this).parent().parent().addClass('active');
        }).mouseup(function() {
          $(this).parent().parent().removeClass('active');
        });
        break;
      case 'file_queued':
        $('#' + file.id).find('.sfwupload-list-cancel').click(function() {
          ref.cancelUpload(file);
        });
        break;
      case 'file_added':
        $('#' + file.fid).find('.sfwupload-list-cancel').unbind('click').click(function() {
          ref.deleteFile(file);
        });
        $('#' + file.fid).find('input:checkbox').bind('click', function() {
          ref.updateStack(file);
        });
        $('#' + file.fid).find('input:text, textarea').blur(function() {
          ref.toggleInput($(this), false, file);
        }).keydown(function(e) {
          ref.key_pressed = e.keyCode;
          if ((e.keyCode == 27) || (e.keyCode == 13 && $(this).get(0).tagName.toLowerCase() !== 'textarea')) {
            $(this).blur();
            return false;
          };
        }).parent().find('span').dblclick(function() {
          ref.toggleInput($(this), true, file);
        });
        break;
      default:
        break;
    };
  };

  /**
   * Toggles editability of text spans inside tablerows
   */
  ref.toggleInput = function(obj, start, file) {
    if (start) {
      obj.hide().parent().find('input:text, textarea').show().focus().select();
    }
    else {
      if (ref.key_pressed == 27) {
        obj.val(obj.parent().find('span').html()).hide().parent().find('span').show();
        return;
      };

      var value = obj.val();
      if (value == '') {
        obj.hide().parent().find('span').html('&nbsp;').show();
      }
      else {
        obj.hide().parent().find('span').text(value).show();
      };
    };
    ref.updateStack(file);
  };

  /**
   * Makes tablerows sortable.
   */
  ref.dragEnable = function() {
    // Move on only if the plugin tableDnD exists.
    if ($.tableDnD == undefined) {
      return;
    };

    var enable = (ref.wrapper_obj.find('tbody tr:not(.hidden)').size() > 1);
    var cursor = (enable) ? 'move' : 'default';
    var visibility = (enable) ? 'visible' : 'hidden';

    $('.sfwupload-list-drag').css({'visibility':visibility}).parents('.drag').css({'cursor':cursor});

    if (!enable) {
      $('.drag').unbind('mousedown');
      return;
    };
    ref.wrapper_obj.tableDnD({
      dragHandle:'drag',
      onDragClass:'dragging',
      onDrop:function() {
        ref.updateStack();
      }
    });
  };

  /**
   * Launched when the swf has been loaded.
   */
  ref.swfUploadLoaded = function() {
    // Update the stats object in order to let SWFUpload know we've already got some files stored
    ref.swfu.setStats({successful_uploads: ref.upload_stack_length});
    ref.addEventHandlers('flash_loaded');
  };

  /**
   * The file(s) have been selected.
   */
  ref.dialogComplete = function(files_selected, files_queued) {
    if (ref.settings.file_upload_limit && ref.settings.file_upload_limit !== 0 && (files_selected > ref.settings.file_upload_limit)) {
      ref.displayMessage(Drupal.t('You can upload only !num !file!', {'!num':ref.settings.file_upload_limit, '!file':Drupal.formatPlural(ref.settings.file_upload_limit, 'file', 'files')}), 'error');
    }
    else {
      ref.uploadNextInQueue();
    };
  };

  /**
   * The file(s) have been selected by the user and added to the upload queue
   */
  ref.fileQueued = function(file) {
    if (ref.settings.file_upload_limit && ref.settings.file_upload_limit !== 0) {
      // Check if the queued file(s) do not exceed the max number of files
      var stats = ref.swfu.getStats();
      if ((ref.upload_stack_length + stats.files_queued) > ref.settings.file_upload_limit) {
        ref.swfu.cancelUpload(file.id);
        var queue_space = (ref.settings.file_upload_limit - ref.upload_stack_length);
        if (queue_space == 0) {
          ref.displayMessage(Drupal.t('You are not allowed to add more than !num !file!', {'!num':ref.settings.file_upload_limit, '!file':Drupal.formatPlural(ref.settings.file_upload_limit, 'file', 'files')}), 'error');
        }
        else {
          ref.displayMessage(Drupal.t('You can upload only !num more !file!', {'!num':queue_space, '!file':Drupal.formatPlural(queue_space, 'file', 'files')}), 'error');
        };
        return;
      };
    };
    if (ref.max_queue_size && ref.max_queue_size !== 0) {
      // Check if the new file does not exceed the max queue size
      if ((ref.upload_stack_size + file.size) > ref.max_queue_size) {
        var max_queue_mbs = ((ref.max_queue_size / 1024) / 1024);
        var file_mbs = ((file.size / 1024) / 1024);
        ref.swfu.cancelUpload(file.id);
        ref.displayMessage(Drupal.t('The file size (!num1 MB) exceeds the upload size (!num2 MB) for this page!', {'!num1':file_mbs.toFixed(2), '!num2':max_queue_mbs.toFixed(2)}), 'error');
        return;
      };
    };
    // No problems found, add the new file to the stack.
    file.extension = ref.getExtension(file.name);
    ref.queue[file.id] = file;
    ref.addFileItem(file);
  };

  /**
   * Responds on file queue errors 
   */
  ref.fileQueueError = function(file, code, message) {
    switch (code) {
      case -110: // The file selected is too large
        var max_file_mbs = ((parseInt(ref.settings.file_size_limit) / 1024) / 1024);
        var file_mbs = ((file.size / 1024) / 1024);
        ref.displayMessage(Drupal.t('The file size (!num1 MB) exceeds the file size limit (!num2 MB)!', {'!num1':file_mbs.toFixed(2), '!num2':max_file_mbs.toFixed(2)}), 'error');
        break;
      default:
        break;
    };
  };

  /**
   * Displays messages
   */
  ref.displayMessage = function(message, type) {
    if (!ref.message_wrapper_obj) {
      ref.message_wrapper_obj = $('<div />').addClass('messages swfupload').insertAfter(ref.wrapper_obj);

      ref.message_wrapper_obj.css({'height':'auto', 'opacity':1}).removeClass('status error').addClass(type).html(message);
      ref.messages_timeout = setTimeout(function() {ref.hideMessages();}, 5000);
    }
    else {
      // The messagewrapper already exists. Add the new message to the wrapper and reset the timeout.

      // Check if the message isn't already displayed
      if (ref.message_wrapper_obj.html().indexOf(message) > -1) {
        return;
      };

      // If the new type differs from the current type, we'll remove the old message.
      if ((ref.message_wrapper_obj.hasClass('status') && type !== 'status') || (ref.message_wrapper_obj.hasClass('error') && type !== 'error')) {
        ref.message_wrapper_obj.removeClass('status error').addClass(type).html(message);
      }
      else {
        ref.message_wrapper_obj.append('<br />' + message);
      };
      clearInterval(ref.messages_timeout);
      ref.messages_timeout = setTimeout(function() {ref.hideMessages();}, 5000);
    };
  };

  /**
   * Slowly hides the messages wrapper
   */
  ref.hideMessages = function() {
    ref.message_wrapper_obj.animate({'height':'0px', 'opacity':0}, 'slow', function() {
      ref.message_wrapper_obj.remove();
      ref.message_wrapper_obj = false;
    });
  };

  /**
   * Triggers a new upload.
   */
  ref.uploadNextInQueue = function() {
		try {
		  ref.swfu.startUpload();
		}
		catch (err) {
		  ref.swfu.debug(err);
		};
  };

  /**
   * Adjusts the progress indicator.
   */
  ref.uploadProgress = function(file, complete, total) {
     // We don't want this one to end up to 100% when all bytes are loaded. The progressbar will have an width of 100% on uploadFileComplete
    var done = Math.round((96 / total)  * complete);
    $('#' + file.id + ' .sfwupload-list-progressbar-status').css({'width':done + '%'});
  };

  /**
   * Handles upload errors
   */
  ref.uploadError = function(file, code, message) {
    // Check for messages which can be handled as 'status' messages
    switch (code) {
      case -240:
        ref.displayMessage(Drupal.t('The upload limit (!num) has been reached!', {'!num':ref.settings.file_upload_limit}), 'status');
        return;
      case -200:
        message = Drupal.t('Server error!', {'!num':ref.settings.file_upload_limit});
    };

    // Give the user some visual indicators of the event
    $('#' + file.id + ' .sfwupload-list-progressbar').addClass('stopped').find('.sfwupload-list-progressbar-status').css({'width':'100%'});
    $('#' + file.id + ' .sfwupload-list-progressbar-glow').append((typeof(message) == 'object' ? message[0] : message));

    // If a file is set, we need to remove the added file DOM element
    if (file) {
      setTimeout(function() {
        ref.removeFileItem(file);
      }, 2000);
    };
  };

  /**
   * Triggered after the upload is succesfully completed.
   */
  ref.uploadComplete = function(file) {
    if (ref.queue[file.id] && !ref.queue[file.id].cancelled) {
      setTimeout(function() {
        $('#' + ref.queue[file.id].fid).find('.sfwupload-list-progressbar').animate({'opacity':0}, "slow", function() {
          file.fid = ref.queue[file.id].fid;
          ref.tableRow(false, file);
          ref.updateStack(file);
          ref.addEventHandlers('file_added', file);
        });
      }, 1000);
    };
		ref.uploadNextInQueue();
  };

  /**
   * Retrieves the data returned by the server
   */
  ref.uploadSuccess = function(file, server_data) {
    var server_data = Drupal.parseJson(server_data);

    // Check for messages returned by the server.
    if (server_data.messages) {

      // Check if the server returned an error
      if (server_data.messages.error) {
        ref.uploadError(file, null, server_data.messages.error);
        ref.queue[file.id].cancelled = true;
        return;
      };

      // Check if the server returned status messages
      if (server_data.messages.status) {
        for (var i in server_data.messages.status) {
          ref.displayMessage(server_data.messages.status[i], 'status');
        };
      };
    };

    // No errors. Complete the fileupload.
    ref.queue[file.id].fid = server_data.file.fid;
    $('#' + file.id).attr({'id':server_data.file.fid}).find('.sfwupload-list-progressbar-status').css({'width':'100%'}).parent().addClass('complete');
  };

  /**
   * Updates the value of the hidden input field which stores all uploaded files
   */
  ref.updateStack = function(file) {
    var fid, input_field, element;
    var old_upload_stack = ref.upload_stack;
    var total_size = 0;
    ref.upload_stack = {};

    ref.wrapper_obj.find('.processed').each(function() {
      fid = $(this).attr('id');

      // If no file is secified, the function is called after sorting
      // There are no new values so the file object is not needed
      // We only need to change the order of the stack 
      if (!file) {
        ref.upload_stack[fid] = old_upload_stack[fid];
      }
      else {
        ref.upload_stack[fid] = {filename:file.filename || file.name, fid:fid};
        total_size += parseInt(file.size);
        for (var name in ref.instance.elements) {
          input_field = $('#edit-' + name + '_' + fid);
          if (input_field.size() !== 0) {
            ref.upload_stack[fid][name] = (input_field.attr('type') == 'checkbox') ? input_field.attr('checked') : input_field.val();
          };
        };
      };
    });
    ref.upload_stack_size = total_size;
    ref.upload_stack_length = ref.objectLength(ref.upload_stack);
    ref.upload_stack_obj.val(ref.toJson(ref.upload_stack));
    ref.dragEnable();

    if (ref.settings.file_upload_limit > ref.upload_stack_length) {
      ref.upload_button_obj.removeClass('disabled').css({opacity:1});
    }
    else {
      ref.upload_button_obj.addClass('disabled').css({opacity:0.4});
    };
  };

  /**
   * Aborts a file upload
   */
  ref.cancelUpload = function(file) {
    // Check if the file is still being uploaded.
    if (ref.swfu.getFile(file.id)) {
      // Abort the upload
      ref.swfu.cancelUpload(file.id);
      ref.queue[file.id].cancelled = true;
      setTimeout(function() {
        ref.removeFileItem(file);
      }, 1000);
    }
  };

  /**
   * Deletes an uploaded file from the list
   */
  ref.deleteFile = function(file) {
    if (confirm(Drupal.t('Are you sure you want to delete !name? This action can not be undone!', {'!name':file.name}))) {
      ref.removeFileItem(file);
    };
  };

  /**
   * Removes a file form the list
   */
  ref.removeFileItem = function(file) {
    var file_tr = $('#' + (file.fid ? file.fid : file.id)).removeClass('processed');
    var file_tds = file_tr.find('td');
    var current_height = file_tr.height();
    var cleared = false;

    // Delete the file from the queue
    delete(ref.queue[file.id]);

    // Animate the deletion of the file's table row
    // First fade out the contents of the td's
    file_tds.find('div, input').animate({opacity:0}, 'fast', function() {
      file_tds.each(function() {
        // The contents are not visible anymore, so we can remove it.
        $(this).html('');
      });

      // Since animate({height:0}) does not work for tr and td's, we need to declare our own interval
      var intv = setInterval(function() {
        current_height -= 5;
        file_tds.height(current_height);
        file_tr.css({opacity: current_height * 4});

        // The animation is complete
        if(current_height <= 5) {
          if (!cleared) {
            cleared = true;
            file_tr.remove();
            clearInterval(intv);

            // Reset the successfull upload queue
            var stats = ref.swfu.getStats();
            stats.successful_uploads--;
            ref.swfu.setStats(stats);

            if (file_tr) {
              // Update the hidden input field
              ref.updateStack(file);
            };
          };
        };
      }, 50);
    });
  };

  /**
   * Retrieve the number of elements in an object
   */
  ref.objectLength = function(obj) {
    if (obj.status !== undefined && obj.status == 0) return 0;

    var count = 0;
    for (var i in obj)
    count++;
    return count;
  };

  /**
   * Parses an object to a json formatted string
   */
  ref.toJson = function(v) {
    switch (typeof v) {
      case 'boolean':
        return v == true ? 'true' : 'false';
      case 'number':
        return v;
      case 'string':
        return '"'+ v.replace(/\n/g, '\\n') +'"';
      case 'object':
        var output = "{";
        for(i in v) {
          output = output + '"' + i + '"' + ":" + ref.toJson(v[i]) + ",";
        }
        output = output.substr(0, output.length - 1) + "}";
        return (output == '}') ? 'null' : output;
      default:
        return 'null';
    };
  };

  /**
   * 
   */
  ref.getExtension = function(file_name) {
    return file_name.substring(file_name.lastIndexOf('.') + 1).toLowerCase();
  };

  /**
   * Replaces default values from ref.instance.elements to file values
   * @see ref.uploadComplete
   */
  ref.replaceMacros = function(value, file) {
    if (!value || value == 0) {
      return false;
    }
    else if (value == 1) {
      return true;
    }
    else {
      var macros = {'{filename}':file.name, '{fid}':file.fid};
      for (var i in macros) {
        value = value.replace(i, macros[i]);
      };
      return value;
    };
  };

  /**
   * Reverses the order of an object
   */
  ref.objReverse = function(obj) {
    var temp_arr = [];
    var temp_obj = {};

    for (var i in obj) {
      temp_arr.push({key:i, data:obj[i]});
    };
    temp_arr = temp_arr.reverse();
    for (var i in temp_arr) {
      temp_obj[temp_arr[i].key] = temp_arr[i].data;
    };    
    return temp_obj;
  };

  return ref;
};

$(function() {
  if (Drupal.settings.swfupload_settings) {
    Drupal.swfu = {};
    var settings = Drupal.settings.swfupload_settings;

    for (var id in settings) {
      Drupal.swfu[id] = new SWFU(id, settings[id]);
      Drupal.swfu[id].init();
    };
  };
});