Hi.
I'm working on a product(install. profile) based on Drupal and I need multiple file upload option so I've created a module for this using plupload(the library not the module).
It's really just a dev version with non-english comments and some messy code but it's working and I'm posting this here so other developers could use my code.
My module differes from this in that it is a widget for image field but tt's also a form element like this module.

As I've said it's not final, not even beta but here it is. I won't be creating a full module/sandbox here on d.org because I did that before and I just don't have the time to maintain.

plup.module

<?php

function plup_menu() {
  $items = array();

  $items['plup'] = array(
    'title' => 'Plupload test page',
    'page callback' => 'plup_page',
    'access callback' => 'plup_upload_access',
    'access arguments' => array('access content'),
  );

  $items['plupload'] = array(
    'title' => 'Plupload upload page',
    'page callback' => 'plup_upload_page',
    'access callback' => 'plup_upload_access',
    'access arguments' => array('access content', 'allow plupload'),
    'type' => MENU_CALLBACK
  );

  $items['plupload/%'] = array(
    'title' => 'Plupload upload page',
    'page callback' => 'plup_upload_page',
    'access callback' => 'plup_upload_access',
    'access arguments' => array('access content', 'allow plupload'),
    'type' => MENU_CALLBACK
  );

  return $items;
}


/**
 * Verifies the token for this request.
 */
function plup_upload_access() {
  foreach(func_get_args() as $permission) {
    if (!user_access($permission)) {
      return FALSE;
    }
  }

  // @todo toto zapracovat aj inde kde je to vhodne
  return !empty($_REQUEST['plupload_token']) && drupal_valid_token($_REQUEST['plupload_token'], 'plupload-handle-uploads');
}


/**
 * _permission
 */
function plup_permission() {
  return array(
    'allow plupload' => array(
      'title' => t('Allow Plupload'),
      'description' => t('Allows user to upload files via plupload.'),
    ),
  );
}


/**
 * _library
 */
function plup_library() {
  $path = drupal_get_path('module', 'plup');

  $lib['plupload'] = array(
    'title' => 'Plupload',
    'website' => 'http://www.plupload.com',
    'version' => '1.5.1.1',
    'js' => array(
      "$path/plupload/js/plupload.full.js" => array(),
      array(
        'type' => 'setting',
        'data' => array(
          'plup' => array(
            'path' => $path
           ),
        ),
      ),
      "$path/plup.js" => array(),
    ),
    'css' => array(
      "$path/plup.css" => array(
        'type' => 'file',
        'media' => 'screen',
      ),
    ),
    'dependencies' => array(
      array('system', 'ui.progressbar'),
      array('system', 'ui.sortable')
    ),
  );

  return $lib;
}


/**
 * _element_info
 */
function plup_element_info() {
  $path = drupal_get_path('module', 'plup');

  $types['plupload_file'] = array(
    '#input' => TRUE,
    '#title' => NULL,
    '#process' => array('plup_process_element'),
    '#value_callback' => 'plup_value_element',
    '#element_validate' => array('plup_validate_element'),
    '#pre_render' => array('plup_pre_render_element'),
    '#default_value' => NULL,
    '#required' => FALSE,
    '#autocomplete_path' => FALSE,
    '#theme_wrappers' => array('form_element'),
    '#theme' => 'plup_plupload',
    '#upload_location' => NULL,
    '#attached' => array(
      'library' => array(
        array('plup', 'plupload')
      )
    ),
    '#plup' => array(
      'container' => 'plupload-container',
      'browse_button' => 'plup-select',
      'upload' => 'plup-upload',
      'runtimes' => 'html5,gears,flash,silverlight,browserplus,html4',
      'max_file_size' => '512MB',
      'url' => url('plupload', array('query' => array('plupload_token' => drupal_get_token('plupload-handle-uploads')))),
      'filters' => array(),
      'chunk_size' => '512K',
      'unique_names' => TRUE,
      'flash_swf_url' => "$path/js/plupload.flash.swf",
      'silverlight_xap_url' => "$path/js/plupload.silverlight.xap",
      'drop_element' => 'plup-filelist',
      'multipart' => FALSE,
      //'multipart_params' => new stdClass(),
      //'required_features' => '',
      //'headers' => new stdClass(),
      //'resize' => $resize,
      //'rename' => TRUE,
      //'sortable' => TRUE,
      'dragdrop' => TRUE,
      'multiple_queues' => TRUE,
      'urlstream_upload' => FALSE,
      'image_style' => 'thumbnail',
      'image_style_path' => ''
    ),
    '#plup_override' => array()
  );

  return $types;
}


/**
 * Value callback needed for removing all items.
 */
function plup_value_element(&$element, $input = FALSE, $form_state = NULL) {
  return ($input !== FALSE) ? $input : array(array('fid' => 0));
}


function plup_process_element($element, &$form_state, $form) {
  $element['#plup']['name'] = $element['#name'];
  $element['#plup'] = array_merge($element['#plup'], $element['#plup_override']);
  $files = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
  $element['#plup']['image_style_path'] = base_path() . $files .'/styles/'. $element['#plup']['image_style'] .'/temporary/';
  drupal_add_js(array('plup' => $element['#plup']), 'setting');
  return $element;
}


function plup_pre_render_element($element) {
  // naloadujem objekty ak su dostupne fid-y
  if (isset($element['#default_value']) && !empty($element['#default_value'])) {
    foreach ($element['#default_value'] AS $delta => $item) {
      $element['#default_value'][$delta] = array_merge($item, (array) file_load($item['fid']));
    }
  }
  return $element;
}


function plup_validate_element($element, &$form_state, $form) {

}


/**
 * _theme
 */
function plup_theme() {
  return array(
    'plup_plupload' => array(
      'render element' => 'element',
    ),
    'plup_items' => array(
      'render element' => 'element'
    )
  );
}


function theme_plup_plupload($variables) {
  $element = $variables['element'];

  $attributes = array();
  if (isset($element['#id'])) {
    $attributes['id'] = $element['#id'];
  }
  if (!empty($element['#attributes']['class'])) {
    $attributes['class'] = (array) $element['#attributes']['class'];
  }
  $attributes['class'][] = 'plupload';

  // This wrapper is required to apply JS behaviors and CSS styling.
  $output = '';
  $output .= '<div' . drupal_attributes($attributes) . '>';
  //$output .= drupal_render_children($element);
  $output .= '<div id="'. $element['#plup']['container'] .'" class="plupload-container">';
  $output .= '<div id="plup-list-wrapper"><ul id="plup-list" class="clearfix">';
  $output .= theme('plup_items', array('element' => $element));
  $output .= '</ul></div>';
  $output .= '<div id="plup-filelist"><table><tr class="plup-drag-info"><td>Drag files here</td></tr></table></div>';
  $output .= '<div id="plup-bar" class="clearfix">';
  $output .= '<a id="plup-select"><div></div>Add</a>';
  $output .= '<a id="plup-upload"><div></div>Upload</a>';
  $output .= '<div id="plup-progress"></div>';
  $output .= '</div>';
  $output .= '</div>';

  $output .= '</div>';
  return $output;
}


function theme_plup_items($vars) {
  $element = &$vars['element'];
  if (isset($element['#default_value']) && !empty($element['#default_value'])) {
    $items = &$element['#default_value'];
  } else {
    return '';
  }

  $attributes = array();
//  if (isset($element['#id'])) {
//    $attributes['id'] = $element['#id'];
//  }
//  if (!empty($element['#attributes']['class'])) {
//    $attributes['class'] = (array) $element['#attributes']['class'];
//  }
//  $attributes['class'][] = 'plupload-item';

  // This wrapper is required to apply JS behaviors and CSS styling.
  $output = '';
  foreach ($items AS $delta => $item) {
    $name = $element['#name'] .'['. $delta .']';
    //$output .= '<div' . drupal_attributes($attributes) . '>';
    $output .= '<li class="ui-state-default">';
    $output .= '<div class="plup-thumb-wrapper">'. theme('image_style', array('style_name' => $element['#plup']['image_style'], 'path' => $item['uri'])) .'</div>';
//    $output .= '<input type="checkbox" class="form-checkbox" name="'. $name .'[display]" checked="checked" value="1" />';
    $output .= '<a class="plup-remove-item"></a>';
    if (isset($element['#plup_override']['title_field']) && $element['#plup_override']['title_field'] == 1) {
      $output .= '<input type="text" class="form-text" name="'. $name .'[title]" value="'. $item['title'] .'" />';
    }
    if (isset($element['#plup_override']['alt_field']) && $element['#plup_override']['alt_field'] == 1) {
      $output .= '<input type="text" class="form-text" name="'. $name .'[alt]" value="'. $item['alt'] .'" />';
    }
    $output .= '<input type="hidden" name="'. $name .'[fid]" value="'. $item['fid'] .'" />';
    $output .= '<input type="hidden" name="'. $name .'[weight]" value="'. $delta .'" />';
    $output .= '</li>';
    //$output .= drupal_render_children($element);
    //$output .= '</div>';
  }
  return $output;
}


function plup_page() {
  $form = drupal_get_form('plup_test_form');
  return $form;
}

function plup_test_form($form, &$form_state) {
  $form['skuska']['#tree'] = TRUE;
  $form['skuska']['field_plupload'] = array(
    '#type' => 'plupload_file',
    '#title' => 'Nejake fajly'
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit')
  );

  return $form;
}


function plup_test_form_submit($form, &$form_state) {
  dsm($form_state);
}



function plup_upload_page() {
  drupal_add_http_header('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT', TRUE);
  drupal_add_http_header('Last-Modified', gmdate("D, d M Y H:i:s") . ' GMT', TRUE);
  drupal_add_http_header('Cache-Control', 'no-store, no-cache, must-revalidate post-check=0, pre-check=0', TRUE);
  drupal_add_http_header('Pragma', 'no-cache', TRUE);

  // Settings
  $targetDir = $GLOBALS['conf']['file_temporary_path'] .'/';
  // 5 minutes execution time
  @set_time_limit(5 * 60);
  // Uncomment this one to fake upload time
  // usleep(5000);

  // Get parameters
  $chunk = isset($_REQUEST['chunk']) ? $_REQUEST['chunk'] : 0;
  $chunks = isset($_REQUEST['chunks']) ? $_REQUEST['chunks'] : 0;
  $filename = isset($_REQUEST['name']) ? $_REQUEST['name'] : '';

  // Clean the fileName for security reasons
  $extensions = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
  $filename = file_munge_filename($filename, $extensions, FALSE);

  // Make sure the fileName is unique but only if chunking is disabled
  if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $filename)) {
    $ext = strrpos($filename, '.');
    $filename_a = substr($filename, 0, $ext);
    $filename_b = substr($filename, $ext);

    $count = 1;
    while (file_exists($targetDir . DIRECTORY_SEPARATOR . $filename_a . '_' . $count . $filename_b))
      $count++;

    $filename = $filename_a . '_' . $count . $filename_b;
  }


  // Look for the content type header
  if (isset($_SERVER['HTTP_CONTENT_TYPE'])) {
    $contentType = $_SERVER['HTTP_CONTENT_TYPE'];
  }

  if (isset($_SERVER['CONTENT_TYPE'])) {
    $contentType = $_SERVER['CONTENT_TYPE'];
  }

  // Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
  if (strpos($contentType, 'multipart') !== false) {
    if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
      // Open temp file
      $out = fopen($targetDir . DIRECTORY_SEPARATOR . $filename, $chunk == 0 ? 'wb' : 'ab');
      if ($out) {
        // Read binary input stream and append it to temp file
        $in = fopen($_FILES['file']['tmp_name'], 'rb');

        if ($in) {
          while ($buff = fread($in, 4096))
            fwrite($out, $buff);
        } else {
          die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
        }

        fclose($in);
        fclose($out);
        @unlink($_FILES['file']['tmp_name']);
      } else {
        die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
      }
    } else {
      die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
    }
  } else {
    // Open temp file
    $out = fopen($targetDir . DIRECTORY_SEPARATOR . $filename, $chunk == 0 ? 'wb' : 'ab');
    if ($out) {
      // Read binary input stream and append it to temp file
      $in = fopen("php://input", 'rb');

      if ($in) {
        while ($buff = fread($in, 4096)) {
          fwrite($out, $buff);
        }
      } else {
        die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
      }

      fclose($in);
      fclose($out);
    } else {
      die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
    }
  }

  if ((isset($_GET['chunk']) && ($_GET['chunk'] + 1) == $_GET['chunks']) || (!isset($_GET['chunk']))) {
    $source = 'temporary://'. $filename;
    $file = new stdClass();
    $file->uid      = $GLOBALS['user']->uid;
    $file->status   = 0;
    $file->filename = $filename;
    $file->uri      = $source;
    $file->filemime = file_get_mimetype($file->filename);
    $file->filesize = filesize($source);
    // doplnim si argument
    $file->arg = arg(1);

    $e = array();
    // tu mozem pracovat so suborom, skontrolvoat typ, presunut ho, overit
    drupal_alter('plup_file_validation', $file, $e);

    if (empty($e)) {
      $return = file_save($file);
    } else {
      drupal_add_http_header('Status', '403 Forbidden', TRUE);
      file_unmanaged_delete($file->uri);
      return;
      $return = new stdclass();
      $return->jsonrpc = '2.0';
      $return->error = new stdclass();
      $return->error->code = 105;
      $return->error->message = implode('<br/>', $e);
      $return->id = 'id';
    }

    drupal_json_output($return);
  }
}


/**
 * Zakladna valdiacia suboru.
 * Standradne pracujem s obrakzami, takze fuiltrujem len je.
 * Ak je nie je dostupny argument, alebo je a je to cislo, tak validujem obrazok.
 */
function plup_plup_file_validation_alter(&$file, &$e) {
  if (!isset($file->arg) || (isset($file->arg) && is_numeric($file->arg))) {
    $instance = db_select('field_config_instance','i')->fields('i', array('data'))->condition('id', $file->arg)->execute()->fetchField();
    $instance = unserialize($instance);
    $settings = $instance['settings'];

    $e += file_validate_is_image($file);
    $e += file_validate_extensions($file, $settings['file_extensions']);
    $e += file_validate_size($file, $settings['max_filesize'], 0);
    $e += file_validate_name_length($file);
    $e += file_validate_image_resolution($file, $settings['max_resolution'], $settings['min_resolution']);
  }
}


/*********************************************
 *            FIELD WIDGET
 ********************************************/

/**
 *  _field_widget_info
 */
function plup_field_widget_info() {
  return array(
    'image_plupload' => array(
      'label' => t('Plupload'),
      'field types' => array('image'),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
        'default value' => FIELD_BEHAVIOR_NONE,
      ),
    ),
  );
}


/**
 * _field_widget_settings_form
 */
function plup_field_widget_settings_form($field, $instance) {
  $styles = array();
  foreach (image_styles() as $name => $style) {
    $styles[$name] = $style['name'];
  }

  $form['image_style'] = array(
    '#type' => 'select',
    '#title' => t('Image style'),
    '#default_value' => isset($instance['widget']['settings']['image_style']) ? $instance['widget']['settings']['image_style'] : 'thumbnail',
    '#options' => $styles
  );

  return $form;
}

/**
 *  _field_widget_form
 */
function plup_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $parents = array_merge(array('values', $element['#field_name'], $element['#language']), $element['#field_parents']);
  $state_data = drupal_array_get_nested_value($form_state, $parents);
dsm($instance);
  if (isset($state_data)) {
    $default_values = $state_data;
  } else if (isset($items)) {
    $default_values = $items;
  } else {
    $default_values = array();
  }

  $images = array(
    '#type' => 'plupload_file',
    '#default_value' => $default_values
  );

  // nastavim povolene pripony suboru
  $ext = new stdClass();
  $ext->title = 'Allowed extensions';
  $ext->extensions = isset($instance['settings']['file_extensions']) ? strtr($instance['settings']['file_extensions'], ' ', ',') : 'jpg,png,gif';
  $images['#plup_override']['filters'] = array($ext);
  // nastavim si maximalnu vlekost suboru
  $images['#plup_override']['max_file_size'] = $instance['settings']['max_filesize'];
  // upravim url aby som vedel id instancie pre ktoru nahravam subory, kvoli kontrole na strane serveru
  $images['#plup_override']['url'] = url('plupload/'. $instance['id'], array('query' => array('plupload_token' => drupal_get_token('plupload-handle-uploads'))));
  // nastavi image style
  $images['#plup_override']['image_style'] = isset($instance['widget']['settings']['image_style']) ? $instance['widget']['settings']['image_style'] : 'thumbnail';
  // nastavenie alt a title poli
  $images['#plup_override']['alt_field'] = $instance['settings']['alt_field'];
  $images['#plup_override']['title_field'] = $instance['settings']['title_field'];

  $element += $images;

  return $element;
}

plup.js

(function ($) {

  // nabindujem si odobratie uz nahraneho suboru
  function plup_remove_item(selector) {
    $(selector).bind('click', function(event) {
      $(this).parent().remove();
    });
  }

  // nabindujem zmenu vlekosti pri kliknuti na input
  function plup_resize_input(selector) {
    $(selector).bind('focus', function(event) {
      $(this).css('z-index', '10').animate({'width': '300px', 'height': '15px'}, 300);
    });
    $(selector).bind('blur', function(event) {
      $(this).removeAttr('style');
    });
  }

  Drupal.behaviors.plup = {
    attach: function(context, settings) {
      // Skript spracujem, len raz. Pouzivam toto namiesto Drupalovskeho .once()
      if (!Drupal.settings.plup.once) {
        /**
         * Pociatocne akcie
         */
        $('#plup-list').sortable(); // Aktivujem sortable
        $('#plup-upload').hide(); // Skryjem tlacidlo pre upload
        var uploader = new plupload.Uploader(Drupal.settings.plup); // Vytvorim objekt pluploadu
        plup_remove_item('.plup-remove-item'); // Ak existuju nejake obrazky, nabindujem tlacidlo pre odobranie
        plup_resize_input('#plup-list > li > input.form-text'); // nabindujem zmenu velkosti inputu na existujuce prvky


        /**
         * Eventy
         */
        // Inicializacia plupload skriptu
        uploader.bind('Init', function(up, params) {
        });

        // Start uploadovania
        uploader.bind('Start', function(up, files) {
        });

        // Zmena stavu uploadovania(start/stop)
        uploader.bind('StateChanged', function(up) {
          if (up.state == 2) {
            $('#plup-progress').progressbar(); // aktivujem hlavny progressbar
          }
          if (up.state == 1) {
            $('#plup-progress').progressbar('destroy'); // znicim progressbar
            $('#plup-upload').hide(); // skryjem tlacidlo pre upload
            $('.plup-drag-info').show(); // zobrazim info o drag
          }
        });

        // Akcie pri pridani suborov do fronty
        uploader.bind('FilesAdded', function(up, files) {
          $('.plup-drag-info').hide(); // skryjem spravu pre drag
          $('#plup-upload').show(); // zobrazim tlacidlo pre upload

          // kazdy subor si zaradim do zoznamu fornty
          $.each(files, function(i, file) {
            $('#plup-filelist table').append('<tr id="' + file.id + '">' +
              '<td class="plup-filelist-file">' +  file.name + '</td>' +
              '<td class="plup-filelist-size">' + plupload.formatSize(file.size) + '</td>' +
              '<td class="plup-filelist-message"></td>' +
              '<td class="plup-filelist-remove"><a class="plup-remove-file"></a></td>' +
              '</tr>');
            // nabindujem si odobratie suboru z fronty
            $('#' + file.id + ' .plup-filelist-remove > a').bind('click', function(event) {
              $('#' + file.id).remove();
              up.removeFile(file);
            });
          });

          up.refresh(); // refresh kvoli flashu alebo silverlightu
        });

        // Ulohy pri odobrani suboru z fronty
        // Tu je chyba. Malo by to byt pole files ale je to objekt file
        // - zla dokumentacia
        uploader.bind('FilesRemoved', function(up, files) {
          // ak neostali ziadne subory vo fronte
          if (up.files.length == 0) {
            $('#plup-upload').hide(); // skryjem tlacidlo pre upload
            $('.plup-drag-info').show(); // zobrazim spravu pre drag
          }
        });

        // Akcie pri uploadovani suborov
        uploader.bind('UploadProgress', function(up, file) {
          // Aktualizacia progress baru
          $('#plup-progress').progressbar({value: uploader.total.percent});
        });

        // Akcie ked nastane chyba
        uploader.bind('Error', function(up, err) {
console.log(err);
          $('#' + err.file.id + ' > .plup-filelist-message').append('<b>Error: ' + err.message + '</b>');
          up.refresh(); // refresh kvoli flashu alebo silverlightu
        });

        // Akcie po dokonceni uploadu 1 suboru z fronty
        uploader.bind('FileUploaded', function(up, file, response) {
          // response je objekt ktory ma response a status parametre, preto 2x repsonse
console.log(response);
          var fileSaved = jQuery.parseJSON(response.response);
          var delta = $('#plup-list li').length;
          var name = Drupal.settings.plup.name +'[' + delta + ']';

          $('#plup-filelist #' + file.id).remove(); // odstranim nahraty subor zo zoznamu fronty
          var title_field = Drupal.settings.plup.title_field ? '<input type="text" class="form-text" name="'+ name + '[title]" value="" />' : '';
          var alt_field = Drupal.settings.plup.alt_field ? '<input type="text" class="form-text" name="'+ name + '[alt]" value="" />' : '';
          // pridam si thumbnail do sachovnice
          $('#plup-list').append(
            '<li class="ui-state-default">' +
            '<div class="plup-thumb-wrapper"><img src="'+ Drupal.settings.plup.image_style_path + file.target_name + '" /></div>' +
//            '<input type="checkbox" class="form-checkbox" name="'+ name + '[display]" checked="checked" value="1" />' +
            '<a class="plup-remove-item"></a>' +
            title_field +
            alt_field +
            '<input type="hidden" name="'+ name + '[fid]" value="' + fileSaved.fid + '" />' +
            '<input type="hidden" name="'+ name + '[weight]" value="' + delta + '" />' +
            '</li>');
          // nabindujem odobratie uz nahrateho suboru
          var new_element = $('input[name="'+ name +'[fid]"]');
          var remove_element = $(new_element).siblings('.plup-remove-item');
          plup_remove_item(remove_element);
          // nabindujem zmenu velkosti inputu
          var text_element = $(new_element).siblings('input.form-text');
          plup_resize_input(text_element);
        });

        // Akcie po dokonceni uploadu vsetkych suborov
        uploader.bind('UploadComplete', function(up, files) {
          $('#plup-list').sortable('refresh'); // refreshnem sortable
          $('.plup-drag-info').show(); // zobrazim spravu pre drag
        });


        /**
         * Dodatocne ulohy
         */
        // Pri kliknuti na 'upload' sa zacnu nahravat subory
        $('#' + Drupal.settings.plup.upload).click(function(e) {
          uploader.start();
          e.preventDefault();
        });

        // Inicializujem plupload
        uploader.init();

        // Pri prehadzovani poradia poloziek sa bude menit ich 'weight' input
        // aby sa zmena ulozila do db
        $('#plup-list').bind('sortupdate', function(event, ui) {
          $(this).find('li').each(function(index) {
            $(this).find('input[name$="[weight]"]').val(index);
          });
        });


//        // Client side form validation
//	$('form').submit(function(e) {
//        var uploader = $('#uploader').plupload('getUploader');
//
//        // Files in queue upload them first
//        if (uploader.files.length > 0) {
//            // When all files are uploaded submit form
//            uploader.bind('StateChanged', function() {
//                if (uploader.files.length === (uploader.total.uploaded + uploader.total.failed)) {
//                    $('form')[0].submit();
//                }
//            });
//
//            uploader.start();
//        } else
//            alert('You must at least upload one file.');
//
//        return false;
//    });


        // Informujem sa ze skript uz bol spracovany
        Drupal.settings.plup.once = 1;
      }
    }
  }

})(jQuery);

plup.css

div.plupload-container {
/*width: 600px;*/
}

#plup-list {
height: auto;
min-height: 120px;
}

#plup-list {
margin: 0px;
padding: 0px;
background-color: #f7f7f7;
}

#plup-list li {
margin: 3px 3px 3px 0;
padding: 1px;
float: left;
width: 100px;
height: 135px;
font-size: 8pt;
list-style: none;
}

.plup-thumb-wrapper {
width: 100px;
height: 100px;
cursor: move;
}

#plup-list li input.form-checkbox {
float: left;
position: relative;
top: -100px;
}

#plup-list li input.form-text {
width: 90px;
height: 12px;
top: -25px;
position: relative;
}

#plup-bar {
background-color: #EEEEEE;
height: 20px;
padding: 5px 10px;
}

#plup-progress {
width: 60px;
height: 10px;
margin-right: 5px;
margin-left: 40px;
float: left;
}

div.plup-file-progressbar {
width: 50px;
height: 12px;
}

#plup-select, #plup-upload, #plup-progress-value {
display: block;
width: 80px;
float: left;
}

#plup-select, #plup-upload,
.plup-remove-file, .plup-remove-item {
cursor: pointer;
}

#plup-select > div, #plup-upload > div {
width: 16px;
height: 16px;
background-repeat: no-repeat;
display: inline-block;
margin-right: 5px;
}

#plup-select > div {
background-image: url('../../../../../misc/ui/images/ui-icons_2e83ff_256x240.png');
background-position: 0px -192px;
}

#plup-upload > div {
background-image: url('../../../../../misc/ui/images/ui-icons_2e83ff_256x240.png');
background-position: -160px -192px;
}

#plup-files-done {
display: block;
width: 50px;
float: right;
}

.ui-progressbar-value {
background-image: none;
background-color: #78E567;
}

.plup-remove-file,
.plup-remove-item {
display: block;
background-image: url('../../../../../misc/ui/images/ui-icons_cd0a0a_256x240.png');
background-repeat: no-repeat;
background-position: -32px -192px;
width: 16px;
height: 16px;
float: right;
}

.plup-remove-item {
opacity: 0.5;
position: relative;
right: 5px;
top: -100px;
}

.plup-remove-item:hover {
opacity: 1;
}

#plup-filelist {
background-color: #f7f7f7;
paddnig: 10px;
min-height: 50px;
border-top: 1px solid #bbb;
max-height: 400px;
overflow: auto;
}

#plup-filelist table {
margin: 0px;
}

#plup-filelist table tbody {
border-top: 0px;
}

#plup-filelist tr:hover td {
background-color: #fafafa;
}

.plup-drag-info {
text-align: center;
font-weight: bold;
}

plup.info

name = Plupload form element & field widget
description = Provides multiple upload functionality as field formatter and form element.
core = 7.x
version = 7.x-1.0

Save in sites/all/modules/plup or wherever you have your modules.
Download the plupload library and put it in plup/plupload/* (not plup/plupload/plupload) and install.

Create an image field and use plupload as widget.

On entity edit form you'll be able to upload through plupload. You can drag files into it, you can upload in multiple queues(=after upload you can select another files and upload again), you can reorder images by draging them to poitions. You can delete images clicking on right upper 'x' icon, you can type title and alt values for images too.

To do: validation on server side, moving files from temp to designated folders, widget settings, error handling(messages)...

My approach is that you can alter any pluplod setting in plupload form element through $element['#plup_override']['key'] = value;

Validation is planned in that way that when script calls plupload/instance_id it will load that instance's settings and valdiate files by them. The 'default' validation is set if instance_id is not provided for custom forms or by using string as argument insted of number as instance id. There is drupal_alter('plup_file_validation', $file, $e); for validation alteration. The default alteration/validation accures only when instance_id(numeric argument) is present.

As I've said it's far from finish but maybe it will help someone.

Please don't write me to do this and that. I don't have time. I will be working on it later. For now I just needed something that works with basic functionality. I don't know when I will be able to finish it but my project has deadline in March so sometime in that period.

I'm posting this here because it is related to plupload library and forum topic isn't appropriate so sorry for another issue :)

PS: You'll probably need to change the css file so images from misc folder have proper paths since my module is in profiles/.. and not sites/all/modules folder.

Comments

Anonymous’s picture

Issue summary: View changes

Added indofmation.

linacio’s picture

Just curious: Why did you write a new module from scratch instead of making changes to the plupload module?

Regards,

Luiz

Anonymous’s picture

I wanted to know exactly what's going on and how things work and you can't do that by using someone else's code. And since this module didn't provide any widget or anything usable and only works with node gallery module I had no use for it. At the beginning I needed a custom solution but with time I was happy with form element and field widget(that was most important for me).

Anonymous’s picture

Here is quite ready version. It moves files to their designated directories after upload and can restrict how many files can be uploaded.

plup.module - http://pastebin.com/GmSNLfCk
plup.css - http://pastebin.com/7YXR7G0R
plup.js - http://pastebin.com/VyUJkcPd

TODO: multiple widgets per page, preserve file names, widget for files, test form element, better validation errors handling

Anonymous’s picture

Status: Active » Closed (fixed)
Anonymous’s picture

Issue summary: View changes

added info