uppie.js
function entriesApi(items, opts, cb) {
    var fd = new FormData(), files = [], rootPromises = [];

    function readEntries(entry, reader, oldEntries, cb) {
      var dirReader = reader || entry.createReader();
      dirReader.readEntries(function(entries) {
        var newEntries = oldEntries ? oldEntries.concat(entries) : entries;
        if (entries.length) {
          setTimeout(readEntries.bind(null, entry, dirReader, newEntries, cb), 0);
        } else {
          cb(newEntries);
        }
      });
    }

    function readDirectory(entry, path, resolve) {
      if (!path) path = entry.name;
      readEntries(entry, 0, 0, function(entries) {
        var promises = [];
        entries.forEach(function(entry) {
          promises.push(new Promise(function(resolve) {
            if (entry.isFile) {
              entry.file(function(file) {
                var p = path + "/" + file.name;
                fd.append(opts.name, file, p);
                files.push(p);
                resolve();
              }, resolve.bind());
            } else readDirectory(entry, path + "/" + entry.name, resolve);
          }));
        });
        Promise.all(promises).then(resolve.bind());
      });
    }

    [].slice.call(items).forEach(function(entry) {
      entry = entry.webkitGetAsEntry();
      if (entry) {
        rootPromises.push(new Promise(function(resolve) {
          if (entry.isFile) {
            entry.file(function(file) {
              fd.append(opts.name, file, file.name);
              files.push(file.name);
              resolve();
            }, resolve.bind());
          } else if (entry.isDirectory) {
            readDirectory(entry, null, resolve);
          }
        }));
      }
    });
    Promise.all(rootPromises).then(cb.bind(null, fd, files));
  }
function entriesApi(items, opts, cb) {
    var fd = new FormData(), files = [], rootPromises = [];

    function readEntries(entry, reader, oldEntries, cb) {
      var dirReader = reader || entry.createReader();
      dirReader.readEntries(function(entries) {
        var newEntries = oldEntries ? oldEntries.concat(entries) : entries;
        if (entries.length) {
          setTimeout(readEntries.bind(null, entry, dirReader, newEntries, cb), 0);
        } else {
          cb(newEntries);
        }
      });
    }

    function readDirectory(entry, path, resolve) {
      if (!path) path = entry.name;
      readEntries(entry, 0, 0, function(entries) {
        var promises = [];
        entries.forEach(function(entry) {
          promises.push(new Promise(function(resolve) {
            if (entry.isFile) {
              entry.file(function(file) {
                var p = path + "/" + file.name;
                fd.append(opts.name, file, p);
                files.push(p);
                resolve();
              }, resolve.bind());
            } else readDirectory(entry, path + "/" + entry.name, resolve);
          }));
        });
        Promise.all(promises).then(resolve.bind());
      });
    }

    [].slice.call(items).forEach(function(entry) {
      entry = entry.webkitGetAsEntry();
      if (entry) {
        rootPromises.push(new Promise(function(resolve) {
          if (entry.isFile) {
            entry.file(function(file) {
              fd.append(opts.name, file, file.name);
              files.push(file.name);
              resolve();
            }, resolve.bind());
          } else if (entry.isDirectory) {
            readDirectory(entry, null, resolve);
          }
        }));
      }
    });
    Promise.all(rootPromises).then(cb.bind(null, fd, files));
  }
function entriesApi ( items , opts , cb ) { var fd = new FormData ( ) , files = [ ] , rootPromises = [ ] ; function readEntries ( entry , reader , oldEntries , cb ) { var dirReader = reader || entry . createReader ( ) ; dirReader . readEntries ( function ( entries ) { var newEntries = oldEntries ? oldEntries . concat ( entries ) : entries ; if ( entries . length ) { setTimeout ( readEntries . bind ( null , entry , dirReader , newEntries , cb ) , 0 ) ; } else { cb ( newEntries ) ; } } ) ; } function readDirectory ( entry , path , resolve ) { if ( ! path ) path = entry . name ; readEntries ( entry , 0 , 0 , function ( entries ) { var promises = [ ] ; entries . forEach ( function ( entry ) { promises . push ( new Promise ( function ( resolve ) { if ( entry . isFile ) { entry . file ( function ( file ) { var p = path + "/" + file . name ; fd . append ( opts . name , file , p ) ; files . push ( p ) ; resolve ( ) ; } , resolve . bind ( ) ) ; } else readDirectory ( entry , path + "/" + entry . name , resolve ) ; } ) ) ; } ) ; Promise . all ( promises ) . then ( resolve . bind ( ) ) ; } ) ; } [ ] . slice . call ( items ) . forEach ( function ( entry ) { entry = entry . webkitGetAsEntry ( ) ; if ( entry ) { rootPromises . push ( new Promise ( function ( resolve ) { if ( entry . isFile ) { entry . file ( function ( file ) { fd . append ( opts . name , file , file . name ) ; files . push ( file . name ) ; resolve ( ) ; } , resolve . bind ( ) ) ; } else if ( entry . isDirectory ) { readDirectory ( entry , null , resolve ) ; } } ) ) ; } } ) ; Promise . all ( rootPromises ) . then ( cb . bind ( null , fd , files ) ) ; }
old drag and drop API implemented in Chrome 11+
old drag and drop API implemented in Chrome 11 +