Преглед на файлове

Merge branch 'master' of https://github.com/transloadit/uppy

Harry Hedger преди 9 години
родител
ревизия
f2cbb34255

+ 1 - 0
package.json

@@ -86,6 +86,7 @@
   "dependencies": {
   "dependencies": {
     "drag-drop": "2.11.0",
     "drag-drop": "2.11.0",
     "tus-js-client": "1.1.3",
     "tus-js-client": "1.1.3",
+    "whatwg-fetch": "^1.0.0",
     "yo-yo": "1.1.1"
     "yo-yo": "1.1.1"
   }
   }
 }
 }

+ 3 - 1
src/plugins/DragDrop.js

@@ -79,7 +79,9 @@ export default class DragDrop extends Plugin {
   handleInputChange () {
   handleInputChange () {
     this.core.log('All right, something selected through input...')
     this.core.log('All right, something selected through input...')
 
 
-    const newFiles = Object.keys(this.input.files).map((key) => this.input.files[key])
+    const newFiles = Object.keys(this.input.files).map((key) => {
+      this.input.files[key]
+    })
 
 
     this.core.emitter.emit('file-add', {
     this.core.emitter.emit('file-add', {
       plugin: this,
       plugin: this,

+ 71 - 52
src/plugins/Formtag.js

@@ -1,67 +1,86 @@
 import Plugin from './Plugin'
 import Plugin from './Plugin'
+import yo from 'yo-yo'
 
 
 export default class Formtag extends Plugin {
 export default class Formtag extends Plugin {
   constructor (core, opts) {
   constructor (core, opts) {
     super(core, opts)
     super(core, opts)
     this.type = 'acquirer'
     this.type = 'acquirer'
-  }
 
 
-  run (results) {
-    console.log({
-      class: 'Formtag',
-      method: 'run',
-      results: results
-    })
+    // Default options
+    const defaultOptions = {
+      target: '.UppyForm',
+      replaceTargetContent: true,
+      multipleFiles: true
+    }
 
 
-    // this.setProgress(0)
+    // Merge default options with the ones set by user
+    this.opts = Object.assign({}, defaultOptions, opts)
+  }
 
 
-    // form FormData
-    // const formData = new FormData(this.dropzone)
-    //
-    // Array.from(files).forEach((file, i) => {
-    //   console.log(`file-${i}`)
-    //   formData.append(`file-${i}`, file)
-    // })
+  handleInputChange (ev) {
+    this.core.log('All right, something selected through input...')
+
+    const files = Object.keys(ev.target.files).map((key) => {
+      return ev.target.files[key]
+    })
 
 
-    const button = document.querySelector(this.opts.doneButtonSelector)
-    var self = this
+    this.core.emitter.emit('file-add', {
+      plugin: this,
+      acquiredFiles: files
+    })
+  }
 
 
-    return new Promise((resolve, reject) => {
-      button.addEventListener('click', (e) => {
-        var fields = document.querySelectorAll(self.opts.selector)
-        var selected = [];
+  render (state) {
+    const next = (ev) => {
+      ev.preventDefault()
+      ev.stopPropagation()
+      this.core.emitter.emit('next')
+    }
 
 
-        [].forEach.call(fields, (field, i) => {
-          selected.push({
-            from: 'Formtag',
-            files: field.files
-          })
-        })
+    return yo`<form class="UppyFormContainer">
+      <input class="UppyForm-input"
+             type="file"
+             name="files[]"
+             onchange=${this.handleInputChange.bind(this)}
+             multiple="${this.opts.multipleFiles ? 'true' : 'false'}">
+      ${!this.core.opts.autoProceed && this.opts.target.name !== 'Modal'
+        ? yo`<button class="UppyForm-uploadBtn UppyNextBtn"
+                     type="submit"
+                     onclick=${next}>
+              ${this.core.i18n('upload')}
+            </button>`
+        : ''}
+    </form>`
+  }
 
 
-        // console.log(fields.length);
-        // for (var i in fields) {
-        //   console.log('i');
-        //   // console.log('i: ', i);
-        //   for (var j in fields[i].files) {
-        //     console.log('j');
-        //     // console.log('i, j', i, j);
-        //     console.log(fields[i].files);
-        //     var file = fields[i].files.item(j);
-        //     if (file) {
-        //       selected.push({
-        //         from: 'Formtag',
-        //         file: fields[i].files.item(j)
-        //       });
-        //     }
-        //   }
-        // }
-        // self.setProgress(100)
-        console.log({
-          selected: selected,
-          fields: fields
-        })
-        resolve(selected)
-      })
-    })
+  install () {
+    this.el = this.render(this.core.state)
+    this.target = this.getTarget(this.opts.target, this, this.el, this.render.bind(this))
   }
   }
+
+  // run (results) {
+  //   console.log({
+  //     class: 'Formtag',
+  //     method: 'run',
+  //     results: results
+  //   })
+  //
+  //   const button = document.querySelector(this.opts.doneButtonSelector)
+  //   var self = this
+  //
+  //   return new Promise((resolve, reject) => {
+  //     button.addEventListener('click', (e) => {
+  //       var fields = document.querySelectorAll(self.opts.selector)
+  //       var selected = [];
+  //
+  //       [].forEach.call(fields, (field, i) => {
+  //         selected.push({
+  //           from: 'Formtag',
+  //           files: field.files
+  //         })
+  //       })
+  //       resolve(selected)
+  //     })
+  //   })
+  // }
 }
 }

+ 2 - 1
src/plugins/GoogleDrive.js

@@ -1,6 +1,7 @@
-import yo from 'yo-yo'
 import Utils from '../core/Utils'
 import Utils from '../core/Utils'
 import Plugin from './Plugin'
 import Plugin from './Plugin'
+import 'whatwg-fetch'
+import yo from 'yo-yo'
 
 
 export default class Google extends Plugin {
 export default class Google extends Plugin {
   constructor (core, opts) {
   constructor (core, opts) {

+ 100 - 49
src/plugins/Multipart.js

@@ -4,75 +4,126 @@ export default class Multipart extends Plugin {
   constructor (core, opts) {
   constructor (core, opts) {
     super(core, opts)
     super(core, opts)
     this.type = 'uploader'
     this.type = 'uploader'
-    if (!this.opts.fieldName === undefined) {
-      this.opts.fieldName = 'files[]'
-    }
-    if (this.opts.bundle === undefined) {
-      this.opts.bundle = true
-    }
-  }
-
-  run (results) {
-    console.log({
-      class: 'Multipart',
-      method: 'run',
-      results: results
-    })
-
-    const files = this.extractFiles(results)
+    this.name = 'Multipart'
 
 
-    // this.setProgress(0)
-    var uploaders = []
-
-    if (this.opts.bundle) {
-      uploaders.push(this.upload(files, 0, files.length))
-    } else {
-      for (let i in files) {
-        uploaders.push(this.upload(files, i, files.length))
-      }
+    // Default options
+    const defaultOptions = {
+      fieldName: 'files[]',
+      responseUrlFieldName: 'url',
+      bundle: true
     }
     }
 
 
-    return Promise.all(uploaders)
+    // Merge default options with the ones set by user
+    this.opts = Object.assign({}, defaultOptions, opts)
   }
   }
 
 
-  upload (files, current, total) {
+  upload (file, current, total) {
+    this.core.log(`uploading ${current} of ${total}`)
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
-      var formPost = new FormData()
-
       // turn file into an array so we can use bundle
       // turn file into an array so we can use bundle
-      if (!this.opts.bundle) {
-        files = [files[current]]
-      }
+      // if (!this.opts.bundle) {
+      //   files = [files[current]]
+      // }
 
 
-      for (let i in files) {
-        formPost.append(this.opts.fieldName, files[i])
-      }
+      // for (let i in files) {
+      //   formPost.append(this.opts.fieldName, files[i])
+      // }
 
 
-      var xhr = new XMLHttpRequest()
-      xhr.open('POST', this.opts.endpoint, true)
+      const formPost = new FormData()
+      formPost.append(this.opts.fieldName, file.data)
 
 
-      xhr.addEventListener('progress', (e) => {
-        var percentage = (e.loaded / e.total * 100).toFixed(2)
-        this.core.log(percentage)
-        // this.setProgress(percentage, current, total)
+      const xhr = new XMLHttpRequest()
+
+      xhr.upload.addEventListener('progress', (ev) => {
+        if (ev.lengthComputable) {
+          let percentage = (ev.loaded / ev.total * 100).toFixed(2)
+          percentage = Math.round(percentage)
+          this.core.log(percentage)
+
+          // Dispatch progress event
+          this.core.emitter.emit('upload-progress', {
+            uploader: this,
+            id: file.id,
+            percentage: percentage
+          })
+        }
       })
       })
 
 
-      xhr.addEventListener('load', () => {
-        var upload = {}
-        if (this.opts.bundle) {
-          upload = {files: files}
-        } else {
-          upload = {file: files[current]}
+      xhr.addEventListener('load', (ev) => {
+        if (ev.target.status === 200) {
+          const resp = JSON.parse(xhr.response)
+          file.uploadURL = resp[this.opts.responseUrlFieldName]
+
+          this.core.log(`Download ${file.name} from ${file.uploadURL}`)
+          return resolve(file)
         }
         }
 
 
-        return resolve(upload)
+        // var upload = {}
+        //
+        // if (this.opts.bundle) {
+        //   upload = {files: files}
+        // } else {
+        //   upload = {file: files[current]}
+        // }
+
+        // return resolve(upload)
       })
       })
 
 
-      xhr.addEventListener('error', () => {
+      xhr.addEventListener('error', (ev) => {
         return reject('fucking error!')
         return reject('fucking error!')
       })
       })
 
 
+      xhr.open('POST', this.opts.endpoint, true)
       xhr.send(formPost)
       xhr.send(formPost)
     })
     })
   }
   }
+
+  run () {
+    const files = this.core.state.files
+
+    const filesForUpload = []
+    Object.keys(files).forEach((file) => {
+      if (files[file].progress === 0) {
+        filesForUpload.push(files[file])
+      }
+    })
+
+    const uploaders = []
+    filesForUpload.forEach((file, i) => {
+      const current = parseInt(i, 10) + 1
+      const total = filesForUpload.length
+      uploaders.push(this.upload(file, current, total))
+    })
+
+    Promise.all(uploaders).then((result) => {
+      this.core.log('Multipart has finished uploading!')
+    })
+
+    //   console.log({
+    //     class: 'Multipart',
+    //     method: 'run',
+    //     results: results
+    //   })
+    //
+    //   const files = results
+    //
+    //   var uploaders = []
+    //
+    //   if (this.opts.bundle) {
+    //     uploaders.push(this.upload(files, 0, files.length))
+    //   } else {
+    //     for (let i in files) {
+    //       uploaders.push(this.upload(files, i, files.length))
+    //     }
+    //   }
+    //
+    //   return Promise.all(uploaders)
+  }
+
+  install () {
+    this.core.emitter.on('next', () => {
+      this.core.log('Multipart is uploading...')
+      this.run()
+    })
+  }
 }
 }

+ 11 - 6
src/plugins/Plugin.js

@@ -29,22 +29,27 @@ export default class Plugin {
 
 
   /**
   /**
    * Check if supplied `target` is a `string` or an `object`.
    * Check if supplied `target` is a `string` or an `object`.
-   * If object (that means its a plugin), search `plugins` for
-   * a plugin with same name and return its target.
+   * If it’s an object — target is a plugin, and we search `plugins`
+   * for a plugin with same name and return its target.
    *
    *
    * @param {String|Object} target
    * @param {String|Object} target
    *
    *
    */
    */
-  getTarget (target, callerPlugin, el, render) {
+  getTarget (target, caller, el, render) {
     if (typeof target === 'string') {
     if (typeof target === 'string') {
-      this.core.log(`Installing ${callerPlugin.name} to ${target}`)
+      this.core.log(`Installing ${caller.name} to ${target}`)
+
+      // clear everything inside the target selector
+      // if (replaceTargetContent) {
+      //   document.querySelector(target).innerHTML = ''
+      // }
       document.querySelector(target).appendChild(el)
       document.querySelector(target).appendChild(el)
 
 
       return target
       return target
     } else {
     } else {
-      this.core.log(`Installing ${callerPlugin.name} to ${target.name}`)
+      this.core.log(`Installing ${caller.name} to ${target.name}`)
       let targetPlugin = this.core.getPlugin(target.name)
       let targetPlugin = this.core.getPlugin(target.name)
-      let selectorTarget = targetPlugin.addTarget(callerPlugin, render)
+      let selectorTarget = targetPlugin.addTarget(caller, render)
 
 
       return selectorTarget
       return selectorTarget
     }
     }

+ 3 - 1
src/plugins/ProgressBar.js

@@ -11,7 +11,9 @@ export default class ProgressBar extends Plugin {
     this.type = 'progressindicator'
     this.type = 'progressindicator'
 
 
     // set default options
     // set default options
-    const defaultOptions = {}
+    const defaultOptions = {
+      replaceTargetContent: false
+    }
 
 
     // merge default options with the ones set by user
     // merge default options with the ones set by user
     this.opts = Object.assign({}, defaultOptions, opts)
     this.opts = Object.assign({}, defaultOptions, opts)

+ 1 - 0
src/plugins/Tus10.js

@@ -9,6 +9,7 @@ export default class Tus10 extends Plugin {
   constructor (core, opts) {
   constructor (core, opts) {
     super(core, opts)
     super(core, opts)
     this.type = 'uploader'
     this.type = 'uploader'
+    this.type = 'Tus'
 
 
     // set default options
     // set default options
     const defaultOptions = {}
     const defaultOptions = {}

+ 4 - 2
test/acceptance/index.js

@@ -25,13 +25,15 @@ var host = isRemoteTest ? 'http://uppi.io' : 'http://localhost:4000'
 // Opera 12 on Linux — didn’t pass
 // Opera 12 on Linux — didn’t pass
 var platforms = [
 var platforms = [
   // { browser: 'Opera', version: '12', os: 'Linux' },
   // { browser: 'Opera', version: '12', os: 'Linux' },
+  // { browser: 'iphone', version: '9.2', os: 'OS X 10.10' },
   { browser: 'firefox', version: '34.0', os: 'Windows 7' },
   { browser: 'firefox', version: '34.0', os: 'Windows 7' },
   { browser: 'chrome', version: '48.0', os: 'Windows XP' }
   { browser: 'chrome', version: '48.0', os: 'Windows XP' }
 ]
 ]
 
 
 var tests = [
 var tests = [
-  require('./i18n.spec.js'),
-  require('./dragdrop.spec.js')
+  require('./multipart.spec.js')
+  // require('./i18n.spec.js'),
+  // require('./dragdrop.spec.js')
 ]
 ]
 
 
 function buildDriver (platform) {
 function buildDriver (platform) {

+ 55 - 0
test/acceptance/multipart.spec.js

@@ -0,0 +1,55 @@
+var test = require('tape')
+var path = require('path')
+var webdriver = require('selenium-webdriver')
+var chalk = require('chalk')
+var By = webdriver.By
+var Driver = require('./Driver')
+var collectErrors = Driver.collectErrors
+
+module.exports = function (driver, platform, host) {
+  test('multipart: upload two files ' +
+      chalk.underline.yellow('[' +
+        platform.os + ' ' +
+        platform.browser + ' ' +
+        platform.version +
+      ']'),
+  function (t) {
+    t.plan(1)
+
+    // Go to the example URL
+    driver.get(host + '/examples/multipart/')
+
+    // Find input by css selector & pass absolute image path to it
+    driver.findElement(By.css('.UppyForm .UppyForm-input')).then(function (el) {
+      el.sendKeys(path.join(__dirname, 'image.jpg'))
+      el.sendKeys(path.join(__dirname, 'image2.jpg'))
+      driver.findElement(By.css('.UppyForm-uploadBtn')).click()
+    })
+
+    function isUploaded () {
+      // .getText() only work on visible elements, so we use .getAttribute('textContent'), go figure
+      // http://stackoverflow.com/questions/21994261/gettext-not-working-on-a-select-from-dropdown
+      return driver.findElement(By.css('.UppyProgressBar .UppyProgressBar-percentage'))
+        .getAttribute('textContent')
+        .then(function (value) {
+          var progress = parseInt(value)
+          var isFileUploaded = progress === 100
+          return isFileUploaded
+        })
+    }
+
+    driver.wait(isUploaded, 15000, 'File image.jpg should be uploaded within 15 seconds')
+      .then(function (result) {
+        collectErrors(driver).then(function () {
+          t.equal(result, true)
+          driver.quit()
+        })
+      })
+      .catch(function (err) {
+        collectErrors(driver).then(function () {
+          t.fail(err)
+          driver.quit()
+        })
+      })
+  })
+}

+ 0 - 61
v0.0.5/multipart.spec.js

@@ -1,61 +0,0 @@
-var test = require('tape')
-var path = require('path')
-var webdriver = require('selenium-webdriver')
-var By = webdriver.By
-
-test('upload two files', function (t) {
-  // Create a new webdriver instance
-  var driver = new webdriver.Builder()
-    .withCapabilities(webdriver.Capabilities.firefox())
-    .build()
-
-  driver.get('http://localhost:4000/examples/multipart/')
-
-  // Select files to upload
-  driver.findElement(By.id('myfile1')).sendKeys(path.join(__dirname, '/image.jpg'))
-  driver.findElement(By.id('myfile2')).sendKeys(path.join(__dirname, '/image2.jpg'))
-
-  // Click submit button
-  driver.findElement(By.id('myupload')).click()
-
-  // Our success/fail message will be logged in the console element
-  var consoleElement = driver.findElement(By.id('console-log'))
-
-  // Wait for our upload message to be logged
-  driver.wait(isUploadComplete.bind(this, consoleElement))
-
-  // Get the result of our upload and test it
-  getElementValue(consoleElement)
-    .then(function (value) {
-      var result = value.split('\n')[0]
-      t.equal(result, 'DEBUG LOG: Upload result -> success!')
-    })
-
-  driver.quit()
-
-  t.end()
-
-  /**
-   * Check if uploading is finished by looking for a result message
-   * in the example's console output element.
-   * @return {Boolean} If uploading is complete
-   */
-  function isUploadComplete (consoleElement) {
-    return getElementValue(consoleElement)
-      .then(function (value) {
-        return value.indexOf('DEBUG LOG: Upload result ->') !== -1
-      })
-  }
-
-  /**
-   * Get value attribute of element
-   * @param  {webdriver.WebElement} element Selenium element object
-   * @return {webdriver.promise} Promise resolving to element value
-   */
-  function getElementValue (element) {
-    return element.getAttribute('value')
-      .then(function (value) {
-        return value
-      })
-  }
-})

+ 5 - 6
website/src/examples/multipart/app.es6

@@ -1,18 +1,17 @@
 import Uppy from 'uppy/core'
 import Uppy from 'uppy/core'
-import { Formtag, Multipart } from 'uppy/plugins'
+import { Formtag, Multipart, Tus10, ProgressBar } from 'uppy/plugins'
 
 
-const uppy = new Uppy({debug: true, wait: false})
+const uppy = new Uppy({debug: true, autoProceed: false})
 
 
 uppy
 uppy
-  .use(Formtag, {
-    selector: '#myform1 [type = "file"],#myform2 [type = "file"]',
-    doneButtonSelector: '#myupload'
-  })
+  .use(Formtag)
   .use(Multipart, {
   .use(Multipart, {
     endpoint: '//api2.transloadit.com',
     endpoint: '//api2.transloadit.com',
     bundle: true,
     bundle: true,
     fieldName: 'files[]'
     fieldName: 'files[]'
   })
   })
+  .use(ProgressBar, {target: 'body'})
+  // .use(Tus10, {endpoint: 'http://master.tus.io:8080/files/'})
   .run()
   .run()
 
 
 console.log('Uppy ' + uppy.type + ' loaded')
 console.log('Uppy ' + uppy.type + ' loaded')

+ 9 - 4
website/src/examples/multipart/app.html

@@ -1,7 +1,12 @@
 <!-- Basic Uppy styles -->
 <!-- Basic Uppy styles -->
 <link rel="stylesheet" href="/uppy/uppy.css">
 <link rel="stylesheet" href="/uppy/uppy.css">
 
 
-<div class="grid">
+<form class="UppyForm" action="//api2.transloadit.com">
+  <input type="file" name="files[]" multiple="">
+  <button type="submit">Upload</button>
+</form>
+
+<!-- <div class="grid">
   <div class="column-1-2">
   <div class="column-1-2">
     <form id="myform1">
     <form id="myform1">
       <input id="myfile1" type="file" name="myfile" multiple />
       <input id="myfile1" type="file" name="myfile" multiple />
@@ -13,8 +18,8 @@
       <input id="myfile2" type="file" name="myfile" multiple />
       <input id="myfile2" type="file" name="myfile" multiple />
     </form>
     </form>
   </div>
   </div>
-</div>
+</div> -->
 
 
-<button id="myupload">
+<!-- <button id="myupload">
   Upload
   Upload
-</button>
+</button> -->