Browse Source

Merge master

Artur Paikin 3 years ago
parent
commit
172cc77f63
62 changed files with 723 additions and 328 deletions
  1. 61 58
      .github/CONTRIBUTING.md
  2. 1 1
      BUNDLE-README.md
  3. 83 84
      README.md
  4. 1 0
      examples/bundled/index.js
  5. 2 2
      examples/cdn-example/index.html
  6. 1 0
      examples/dev/Dashboard.js
  7. 1 1
      examples/transloadit-textarea/index.html
  8. 2 2
      examples/uppy-with-companion/client/index.html
  9. 16 16
      package-lock.json
  10. 1 1
      package.json
  11. 6 3
      packages/@uppy/angular/package.json
  12. 1 1
      packages/@uppy/aws-s3/package.json
  13. 1 0
      packages/@uppy/aws-s3/src/MiniXHRUpload.js
  14. 1 1
      packages/@uppy/companion/package.json
  15. 1 1
      packages/@uppy/companion/src/server/Uploader.js
  16. 1 1
      packages/@uppy/core/package.json
  17. 60 2
      packages/@uppy/core/src/index.js
  18. 1 1
      packages/@uppy/core/src/index.test.js
  19. 1 1
      packages/@uppy/dashboard/package.json
  20. 8 1
      packages/@uppy/dashboard/src/components/EditorPanel.js
  21. 29 16
      packages/@uppy/dashboard/src/components/FileCard/index.js
  22. 1 1
      packages/@uppy/dashboard/src/components/PickerPanelContent.js
  23. 14 1
      packages/@uppy/dashboard/src/index.js
  24. 2 1
      packages/@uppy/dashboard/src/style.scss
  25. 2 0
      packages/@uppy/dashboard/types/index.d.ts
  26. 1 1
      packages/@uppy/drag-drop/package.json
  27. 1 1
      packages/@uppy/image-editor/package.json
  28. 5 25
      packages/@uppy/image-editor/src/Editor.js
  29. 27 13
      packages/@uppy/image-editor/src/index.js
  30. 1 1
      packages/@uppy/locales/package.json
  31. 1 1
      packages/@uppy/locales/src/de_DE.js
  32. 2 0
      packages/@uppy/locales/src/en_US.js
  33. 1 1
      packages/@uppy/react/package.json
  34. 2 2
      packages/@uppy/robodog/README.md
  35. 1 1
      packages/@uppy/robodog/package.json
  36. 55 0
      packages/@uppy/robodog/types/index.d.ts
  37. 83 0
      packages/@uppy/robodog/types/index.test-d.ts
  38. 1 1
      packages/@uppy/screen-capture/package.json
  39. 1 1
      packages/@uppy/svelte/package.json
  40. 1 1
      packages/@uppy/transloadit/package.json
  41. 84 1
      packages/@uppy/transloadit/types/index.d.ts
  42. 1 1
      packages/@uppy/vue/package.json
  43. 1 1
      packages/@uppy/webcam/package.json
  44. 1 1
      packages/uppy/package.json
  45. 2 0
      test/endtoend/providers/main.js
  46. 2 0
      test/endtoend/typescript/main.ts
  47. 102 51
      website/src/_template/contributing.md
  48. 1 0
      website/src/docs/box.md
  49. 6 5
      website/src/docs/dashboard.md
  50. 1 1
      website/src/docs/drop-target.md
  51. 5 5
      website/src/docs/index.md
  52. 2 2
      website/src/docs/locales.md
  53. 4 4
      website/src/docs/robodog-form.md
  54. 2 2
      website/src/docs/robodog.md
  55. 2 0
      website/src/docs/uppy.md
  56. 11 1
      website/src/examples/dashboard/app.es6
  57. 3 0
      website/src/examples/dashboard/app.html
  58. 4 1
      website/src/examples/dashboard/index.ejs
  59. 3 3
      website/src/examples/i18n/app.html
  60. 1 1
      website/src/examples/markdown-snippets/app.es6
  61. 1 1
      website/src/examples/markdown-snippets/app.html
  62. 3 2
      website/themes/uppy/layout/index.ejs

+ 61 - 58
.github/CONTRIBUTING.md

@@ -14,8 +14,8 @@ Our website’s examples section is also our playground, please read the [Local
 
 
 ### Requiring files
 ### Requiring files
 
 
-- If we are `require()`ing a file from the same subpackage (e.g. require `@uppy/dashboard/utils/hi.js` from `@uppy/dashboard/src/index.js`) - we can freely use relative imports, as long as the required file is under the `src` directory (`/:packageName/src/**/*.js`).
-- But if we want to require some file from another subpackage - we should use global @uppy requires, and they should always be in the form of `@uppy/:packageName/(lib instead of src)/(same path).js`
+*   If we are `require()`ing a file from the same subpackage, we can freely use relative imports as long as the required file is under the `src` directory (for example to import `@uppy/dashboard/src/utils/hi.js` from `@uppy/dashboard/src/index.js`, use `require('./utils/hi.js')`).
+*   But if we want to `require()` some file from another subpackage - we should use global @uppy requires, and they should always be in the form of `@uppy/:packageName/(lib instead of src)/(same path).js`
 
 
 ## Tests
 ## Tests
 
 
@@ -25,7 +25,7 @@ Unit tests are using Jest and can be run with:
 npm run test:unit
 npm run test:unit
 ```
 ```
 
 
-For end-to-end tests, we use [Webdriverio](http://webdriver.io). For it to run locally, you need to install a Selenium standalone server. Just follow [the guide](http://webdriver.io/guide.html) to do so. You can also install a Selenium standalone server from NPM:
+For end-to-end tests, we use [Webdriverio](http://webdriver.io). For it to run locally, you need to install a Selenium standalone server. Follow [the Webdriverio guide](http://webdriver.io/guide.html) to do so. You can also install a Selenium standalone server from NPM:
 
 
 ```bash
 ```bash
 npm install selenium-standalone -g
 npm install selenium-standalone -g
@@ -52,13 +52,13 @@ npm run test:endtoend:local -- -b chrome
 
 
 > Note: The `--` is important, it tells npm that the remaining arguments should be interpreted by the script itself, not by npm.
 > Note: The `--` is important, it tells npm that the remaining arguments should be interpreted by the script itself, not by npm.
 
 
-You can run in multiple browsers by passing multiple `-b` flags:
+You can run in several browsers by passing several `-b` flags:
 
 
 ```bash
 ```bash
 npm run test:endtoend:local -- -b chrome -b firefox
 npm run test:endtoend:local -- -b chrome -b firefox
 ```
 ```
 
 
-When trying to get a specific integration test to pass, it's not that helpful to continuously run _all_ tests. You can use the `--suite` flag to run tests from a single `./test/endtoend` folder. For example, `--suite thumbnails` will only run the tests from `./test/endtoend/thumbnails`. Of course, it can also be combined with one or more `-b` flags.
+When trying to get a specific integration test to pass, its not that helpful to continuously run _all_ tests. You can use the `--suite` flag to run tests from a single `./test/endtoend` folder. For example, `--suite thumbnails` will only run the tests from `./test/endtoend/thumbnails`. It can also be used in conjunction with one or more `-b` flags.
 
 
 ```bash
 ```bash
 npm run test:endtoend:local -- -b chrome --suite thumbnails
 npm run test:endtoend:local -- -b chrome --suite thumbnails
@@ -70,26 +70,27 @@ These tests are also run automatically on Travis builds with [SauceLabs](https:/
 
 
 ### Instagram integration
 ### Instagram integration
 
 
-Even though facebook [allows using](https://developers.facebook.com/blog/post/2018/06/08/enforce-https-facebook-login/) http://localhost in dev mode, Instagram doesn't seem to support that, and seems to need a publically available domain name with HTTPS.
+Even though facebook [allows using](https://developers.facebook.com/blog/post/2018/06/08/enforce-https-facebook-login/) http://localhost in dev mode, Instagram doesnt seem to support that, and seems to need a publically available domain name with HTTPS.
 
 
 Make sure that you are using a development facebook app at <https://developers.facebook.com/apps>
 Make sure that you are using a development facebook app at <https://developers.facebook.com/apps>
 
 
-Go to "Instagram Basic Display" and find `Instagram App ID` and `Instagram App Secret`. Put them in a file called `env.sh` in the repo root:
+Go to “Instagram Basic Display” and find `Instagram App ID` and `Instagram App Secret`. Put them in a file called `env.sh` in the repo root:
+
 ```bash
 ```bash
 export COMPANION_INSTAGRAM_KEY="Instagram App ID"
 export COMPANION_INSTAGRAM_KEY="Instagram App ID"
 export COMPANION_INSTAGRAM_SECRET="Instagram App Secret"
 export COMPANION_INSTAGRAM_SECRET="Instagram App Secret"
 ```
 ```
 
 
 Run
 Run
+
 ```bash
 ```bash
 ngrok http 3020
 ngrok http 3020
 ```
 ```
-Note the ngrok https base URL, e.g. `https://e0c7de09808d.ngrok.io` and
-append `/instagram/redirect` to it, e.g.:
 
 
-```
-https://e0c7de09808d.ngrok.io/instagram/redirect
-```
+Note the ngrok https base URL, for example `https://e0c7de09808d.ngrok.io` and
+append `/instagram/redirect` to it, such as:
+
+    https://e0c7de09808d.ngrok.io/instagram/redirect
 
 
 Add this full ngrok URL to `Valid OAuth Redirect URIs` under `Instagram Basic Display`.
 Add this full ngrok URL to `Valid OAuth Redirect URIs` under `Instagram Basic Display`.
 
 
@@ -102,7 +103,7 @@ COMPANION_PROTOCOL="https"
 
 
 Edit `examples/dev/Dashboard.js`:
 Edit `examples/dev/Dashboard.js`:
 
 
-```
+```js
 const COMPANION_URL = 'https://e0c7de09808d.ngrok.io'
 const COMPANION_URL = 'https://e0c7de09808d.ngrok.io'
 ```
 ```
 
 
@@ -137,37 +138,37 @@ Releases are managed by [Lerna](https://github.com/lerna/lerna). We do some clea
 npm run release
 npm run release
 ```
 ```
 
 
-If you have two-factor authentication enabled on your account, Lerna will ask for a one-time password. There is an issue with the CLI where the OTP prompt may be obscured by a publishing progress bar. If Lerna appears to hang just as it starts publishing, chances are it's waiting for the password. Try typing in your OTP and hitting enter.
+If you have two-factor authentication enabled on your account, Lerna will ask for a one-time password. You may stumble upon a known issue with the CLI where the OTP prompt may be obscured by a publishing progress bar. If Lerna appears to freeze as it starts publishing, chances are it’s waiting for the password. Try typing in your OTP and hitting enter.
 
 
 Other things to keep in mind during release:
 Other things to keep in mind during release:
 
 
-* When doing a major release >= 1.0, of the `@uppy/core` package, the `peerDependency` of the plugin packages needs to be updated first. Eg when updating from 1.y.z to 2.0.0, the peerDependency of each should be `"@uppy/core": "^2.0.0"` before doing `npm run release`.
-* When adding a new package, add the following key to its package.json:
-  ```json
-  "publishConfig": { "access": "public" }
-  ```
-  Else, npm will try and fail to publish a _private_ package, because the `@uppy` scope on npm does not support that.
+*   When doing a major release >= 1.0, of the `@uppy/core` package, the `peerDependency` of the plugin packages needs to be updated first. Eg when updating from 1.y.z to 2.0.0, the peerDependency of each should be `"@uppy/core": "^2.0.0"` before doing `npm run release`.
+*   When adding a new package, add the following key to its package.json:
+    ```json
+    "publishConfig": { "access": "public" }
+    ```
+    Else, npm will try and fail to publish a _private_ package, because the `@uppy` scope on npm does not support that.
 
 
 After a release, the demos on transloadit.com should also be updated. After updating, check that some things work locally:
 After a release, the demos on transloadit.com should also be updated. After updating, check that some things work locally:
 
 
- - the demos in the demo section work (try one that uses an import robot, and one that you need to upload to)
- - the demos on the homepage work and can import from Google Drive, Instagram, Dropbox, etc.
+*   the demos in the demo section work (try one that uses an import robot, and one that you need to upload to)
+*   the demos on the homepage work and can import from Google Drive, Instagram, Dropbox, etc.
 
 
-If you don't have access to the transloadit.com source code ping @arturi or @goto-bus-stop and we'll pick it up. :sparkles:
+If you don’t have access to the transloadit.com source code ping @arturi or @goto-bus-stop and we’ll pick it up. :sparkles:
 
 
 ## Website development
 ## Website development
 
 
-We keep the [uppy.io](http://uppy.io) website in `./website`, so it’s easy to keep docs and code in sync as we are still iterating at high velocity.
+We keep the [uppy.io](http://uppy.io) website in `./website` to keep docs and code in sync as we are still iterating at high velocity.
 
 
-The site is built with [Hexo](http://hexo.io/), and Travis automatically deploys this onto GitHub Pages (it overwrites the `gh-pages` branch with Hexo's build at every change to `master`). The content is written in Markdown and located in `./website/src`. Feel free to fork & hack!
+The site is built with [Hexo](http://hexo.io/), and Travis automatically deploys this onto GitHub Pages (it overwrites the `gh-pages` branch with Hexos build at every change to `master`). The content is written in Markdown and located in `./website/src`. Feel free to fork & hack!
 
 
 Even though bundled in this repo, the website is regarded as a separate project. As such, it has its own `package.json` and we aim to keep the surface where the two projects interface as small as possible. `./website/update.js` is called during website builds to inject the Uppy knowledge into the site.
 Even though bundled in this repo, the website is regarded as a separate project. As such, it has its own `package.json` and we aim to keep the surface where the two projects interface as small as possible. `./website/update.js` is called during website builds to inject the Uppy knowledge into the site.
 
 
 ### Local previews
 ### Local previews
 
 
-1. `npm install`
-1. `npm start`
-1. Go to http://localhost:4000. Your changes in `/website` and `/packages/@uppy` will be watched, your browser will refresh as files change.
+1.  `npm install`
+2.  `npm start`
+3.  Go to http://localhost:4000. Your changes in `/website` and `/packages/@uppy` will be watched, your browser will refresh as files change.
 
 
 Then, to work on, for instance, the XHRUpload example, you would edit the following files:
 Then, to work on, for instance, the XHRUpload example, you would edit the following files:
 
 
@@ -182,7 +183,7 @@ And open <http://localhost:4000/examples/xhrupload/> in your web browser.
 
 
 ## CSS guidelines
 ## CSS guidelines
 
 
-The CSS standards followed in this project closely resemble those from [Medium's CSS Guidelines](https://gist.github.com/fat/a47b882eb5f84293c4ed). If something is not mentioned here, follow their guidelines.
+The CSS standards followed in this project closely resemble those from [Mediums CSS Guidelines](https://gist.github.com/fat/a47b882eb5f84293c4ed). If something is not mentioned here, follow their guidelines.
 
 
 ### Naming conventions
 ### Naming conventions
 
 
@@ -221,7 +222,7 @@ Syntax: `[<namespace>-]<ComponentName>[-descendentName][--modifierName]`
 
 
 ### SASS
 ### SASS
 
 
-This project uses SASS, with some limitations on nesting.  One-level-deep nesting is allowed, but nesting may not extend a selector by using the `&` operator.  For example:
+This project uses SASS, with some limitations on nesting. One-level-deep nesting is allowed, but nesting may not extend a selector by using the `&` operator. For example:
 
 
 ```sass
 ```sass
 /* BAD */
 /* BAD */
@@ -247,11 +248,11 @@ Style to the mobile breakpoint with your selectors, then use `min-width` media q
 
 
 ### Selector, rule ordering
 ### Selector, rule ordering
 
 
-- All selectors are sorted alphabetically and by type.
-- HTML elements go above classes and IDs in a file.
-- Rules are sorted alphabetically.
+*   All selectors are sorted alphabetically and by type.
+*   HTML elements go above classes and IDs in a file.
+*   Rules are sorted alphabetically.
 
 
-```sass
+```scss
 /* BAD */
 /* BAD */
 .wrapper {
 .wrapper {
   width: 940px;
   width: 940px;
@@ -287,28 +288,29 @@ h1 {
 
 
 Before opening a pull request for the new integration, open an issue to discuss said integration with the Uppy team. After discussing the integration, you can get started on it. First off, you need to construct the basic components for your integration. The following components are the current standard:
 Before opening a pull request for the new integration, open an issue to discuss said integration with the Uppy team. After discussing the integration, you can get started on it. First off, you need to construct the basic components for your integration. The following components are the current standard:
 
 
-- `Dashboard`: Inline Dashboard (`inline: true`)
-- `DashboardModal`: Dashboard as a modal
-- `DragDrop`
-- `ProgressBar`
-- `StatusBar`
+*   `Dashboard`: Inline Dashboard (`inline: true`)
+*   `DashboardModal`: Dashboard as a modal
+*   `DragDrop`
+*   `ProgressBar`
+*   `StatusBar`
 
 
-All of these components should function as references to the normal component. Depending on how the framework you're using handles references to the DOM, your approach to creating these may be different. For example, in React, you can assign a property of the component to the reference of a component ([see here](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/packages/%40uppy/react/src/Dashboard.js#L47-L54)). This may differ in your framework, but from what we've found, the concepts are generally pretty similar.
+All these components should function as references to the normal component. Depending on how the framework youre using handles references to the DOM, your approach to creating these may be different. For example, in React, you can assign a property of the component to the reference of a component ([see here](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/packages/%40uppy/react/src/Dashboard.js#L47-L54)). This may differ in your framework, but from what weve found, the concepts are generally pretty similar.
 
 
-If you're familiar with React, Vue or soon Svelte, it might be useful to read through the code of those integrations, as they lay out a pretty good structure. After the basic components have been built, there are a few more important tasks to get done:
+If youre familiar with React, Vue or soon Svelte, it might be useful to read through the code of those integrations, as they lay out a pretty good structure. After the basic components have been built, here are a few more important tasks to get done:
 
 
-- Add TypeScript support in some capacity (if possible)
-- Write documentation
-- Add an example
-- Configuring the build system
+*   Add TypeScript support in some capacity (if possible)
+*   Write documentation
+*   Add an example
+*   Configuring the build system
 
 
 ### Common issues
 ### Common issues
 
 
-Before going into these tasks, there are a few common gotchas that you should be aware of.
+Before going into these tasks, here are a few common gotchas that you should be aware of.
 
 
 #### Dependencies
 #### Dependencies
 
 
 Your `package.json` should resemble something like this:
 Your `package.json` should resemble something like this:
+
 ```json
 ```json
 {
 {
   "name": "@uppy/framework",
   "name": "@uppy/framework",
@@ -333,20 +335,21 @@ The most important part about this is that `@uppy/core` is a peer dependency. If
 
 
 ### Adding TypeScript Support
 ### Adding TypeScript Support
 
 
-This section won't be too in-depth, because TypeScript depends on your framework. As general advice, prefer using `d.ts` files and vanilla JavaScript over TypeScript files. This is of course circumstantial, but it makes handling the build system a lot easier when TypeScript doesn't have to transpiled. The version of typescript in the monorepo is `3.7.5`, so features like `import type` will not work at build time. For upcoming integrations, like Angular, this may be updated.
+This section wont be too in-depth, because TypeScript depends on your framework. As general advice, prefer using `d.ts` files and vanilla JavaScript over TypeScript files. This is circumstantial, but it makes handling the build system a lot easier when TypeScript doesn’t have to transpiled. The version of typescript in the monorepo is `4.1`.
 
 
 ### Writing docs
 ### Writing docs
 
 
 Generally, documentation for integrations can be broken down into a few pieces that apply to every component, and then documentation for each component. The structure should look something like this:
 Generally, documentation for integrations can be broken down into a few pieces that apply to every component, and then documentation for each component. The structure should look something like this:
 
 
-- Installation
-- Initializing Uppy (may vary depending on how the framework handles reactivity)
-- Usage
-- *For each component*
-  - Loading CSS
-  - Props
+*   Installation
+*   Initializing Uppy (may vary depending on how the framework handles reactivity)
+*   Usage
+*   _For each component_
+    *   Loading CSS
+    *   Props
 
 
 It may be easier to copy the documentation of earlier integrations and change the parts that need to be changed rather than writing this from scratch. Preferably, keep the documentation to one page. For the front-matter, write something like:
 It may be easier to copy the documentation of earlier integrations and change the parts that need to be changed rather than writing this from scratch. Preferably, keep the documentation to one page. For the front-matter, write something like:
+
 ```markdown
 ```markdown
 title: Framework Name
 title: Framework Name
 type: docs
 type: docs
@@ -355,17 +358,17 @@ order: 0
 category: "Other Integrations"
 category: "Other Integrations"
 ```
 ```
 
 
-This data is used to generate Uppy's website. Refer to [the section about running the website locally](#website-previews) if you'd like to see how the docs look on the website.
+This data is used to generate Uppy’s website. Refer to [the section about running the website locally](#website-previews) if you’d like to see how the docs look on the website.
 
 
 ### Adding an example
 ### Adding an example
 
 
-This is pretty simple to do, as you can likely use whatever code generation tool for your framework (ex. `create-react-app`) to create this example. Make sure you add the same version of `@uppy/core` to this as your peer dependency required, or you may run into strange issues. Try to include all of the components are some of their functionality. [The React example](https://github.com/transloadit/uppy/blob/master/examples/react-example/App.js) is a great... well example of how to do this well.
+You can likely use whatever code generation tool for your framework (ex. `create-react-app`) to create this example. Make sure you add the same version of `@uppy/core` to this as your peer dependency required, or you may run into strange issues. Try to include all the components are some of their functionality. [The React example](https://github.com/transloadit/uppy/blob/master/examples/react-example/App.js) is a great... well example of how to do this well.
 
 
 ### Integrating the build system
 ### Integrating the build system
 
 
-The biggest part of this is understanding Uppy's build system. The high level description is basically `babel` goes through almost all of the packages and transpiles all the Javascript files in the `src` directory to more compatible JavaScript in the `lib` folder. If you're using vanilla JavaScript for your integration (like React and Vue do), then you can just use this build system and use the files generated as your entry points. 
+The biggest part of this is understanding Uppy’s build system. The high level description is that `babel` goes through almost all the packages and transpiles all the Javascript files in the `src` directory to more compatible JavaScript in the `lib` folder. If youre using vanilla JavaScript for your integration (like React and Vue do), then you can use this build system and use the files generated as your entry points.
 
 
-If you're using some kind of more abstract file format (like Svelte), then you probably want do to a few things: add the directory name to [this `IGNORE` regex](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/bin/build-lib.js#L15); add all of your build dependencies to the root `package.json` (try to keep this small); add a new `build:framework` script to the root `package.json`. This script usually looks something like this:
+If youre using some kind of more abstract file format (like Svelte), then you probably want do to a few things: add the directory name to [this `IGNORE` regex](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/bin/build-lib.js#L15); add all your build dependencies to the root `package.json` (try to keep this small); add a new `build:framework` script to the root `package.json`. This script usually looks something like this:
 
 
 ```json
 ```json
 {
 {
@@ -375,4 +378,4 @@ If you're using some kind of more abstract file format (like Svelte), then you p
 }
 }
 ```
 ```
 
 
-Then, add this script to the `build:js` script. Try running the `build:js` script and make sure it does not error. It may also be of use to ensure that global dependencies aren't being used (ex. not having rollup locally and relying on a global install), as these dependencies won't be present on the machine's handling building.
+Then, add this script to the `build:js` script. Try running the `build:js` script and make sure it does not error. It may also be of use to make sure that global dependencies aren’t being used (ex. not having rollup locally and relying on a global install), as these dependencies won’t be present on the machine’s handling building.

+ 1 - 1
BUNDLE-README.md

@@ -1,7 +1,7 @@
 # Uppy
 # Uppy
 
 
 Hi, thanks for trying out the bundled version of the Uppy File Uploader. You can use
 Hi, thanks for trying out the bundled version of the Uppy File Uploader. You can use
-this from a CDN (e.g. `<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>`) or bundle it with your webapp. 
+this from a CDN (e.g. `<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>`) or bundle it with your webapp. 
 
 
 Note that the recommended way to use Uppy is to install it with yarn/npm and use a 
 Note that the recommended way to use Uppy is to install it with yarn/npm and use a 
 bundler like Webpack so that you can create a smaller custom build with just the
 bundler like Webpack so that you can create a smaller custom build with just the

+ 83 - 84
README.md

@@ -69,7 +69,7 @@ $ npm install @uppy/core @uppy/dashboard @uppy/tus
 
 
 We recommend installing from npm and then using a module bundler such as [Webpack](https://webpack.js.org/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
 We recommend installing from npm and then using a module bundler such as [Webpack](https://webpack.js.org/), [Browserify](http://browserify.org/) or [Rollup.js](http://rollupjs.org/).
 
 
-Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css), either to your HTML page's `<head>` or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
+Add CSS [uppy.min.css](https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css), either to your HTML page's `<head>` or include in JS, if your bundler of choice supports it — transforms and plugins are available for Browserify and Webpack.
 
 
 Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object.
 Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object.
 
 
@@ -77,10 +77,10 @@ Alternatively, you can also use a pre-built bundle from Transloadit's CDN: Edgly
 
 
 ```html
 ```html
 <!-- 1. Add CSS to `<head>` -->
 <!-- 1. Add CSS to `<head>` -->
-<link href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css" rel="stylesheet">
+<link href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css" rel="stylesheet">
 
 
 <!-- 2. Add JS before the closing `</body>` -->
 <!-- 2. Add JS before the closing `</body>` -->
-<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
 
 
 <!-- 3. Initialize -->
 <!-- 3. Initialize -->
 <div class="UppyDragDrop"></div>
 <div class="UppyDragDrop"></div>
@@ -190,8 +190,7 @@ If you're using Uppy from CDN, those polyfills are already included in the legac
 bundle, so no need to include anything additionally:
 bundle, so no need to include anything additionally:
 
 
 ```html
 ```html
-<script nomodule src="https://releases.transloadit.com/uppy/v2.0.0/uppy.legacy.min.js"></script>
-<script type="module">import"https://releases.transloadit.com/uppy/v2.0.0/uppy.min.js";</script>
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
 ```
 ```
 
 
 ## FAQ
 ## FAQ
@@ -256,25 +255,25 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
 [nqst](https://github.com/nqst) |[lakesare](https://github.com/lakesare) |[kiloreux](https://github.com/kiloreux) |[sadovnychyi](https://github.com/sadovnychyi) |[samuelayo](https://github.com/samuelayo) |[richardwillars](https://github.com/richardwillars) |
 [nqst](https://github.com/nqst) |[lakesare](https://github.com/lakesare) |[kiloreux](https://github.com/kiloreux) |[sadovnychyi](https://github.com/sadovnychyi) |[samuelayo](https://github.com/samuelayo) |[richardwillars](https://github.com/richardwillars) |
 
 
-[<img alt="ajkachnic" src="https://avatars.githubusercontent.com/u/44317699?v=4&s=117" width="117">](https://github.com/ajkachnic) |[<img alt="dependabot[bot]" src="https://avatars.githubusercontent.com/in/29110?v=4&s=117" width="117">](https://github.com/apps/dependabot) |[<img alt="zcallan" src="https://avatars.githubusercontent.com/u/13760738?v=4&s=117" width="117">](https://github.com/zcallan) |[<img alt="tim-kos" src="https://avatars.githubusercontent.com/u/15005?v=4&s=117" width="117">](https://github.com/tim-kos) |[<img alt="janko" src="https://avatars.githubusercontent.com/u/795488?v=4&s=117" width="117">](https://github.com/janko) |[<img alt="wilkoklak" src="https://avatars.githubusercontent.com/u/17553085?v=4&s=117" width="117">](https://github.com/wilkoklak) |
+[<img alt="ajkachnic" src="https://avatars.githubusercontent.com/u/44317699?v=4&s=117" width="117">](https://github.com/ajkachnic) |[<img alt="dependabot[bot]" src="https://avatars.githubusercontent.com/in/29110?v=4&s=117" width="117">](https://github.com/apps/dependabot) |[<img alt="zcallan" src="https://avatars.githubusercontent.com/u/13760738?v=4&s=117" width="117">](https://github.com/zcallan) |[<img alt="aduh95" src="https://avatars.githubusercontent.com/u/14309773?v=4&s=117" width="117">](https://github.com/aduh95) |[<img alt="tim-kos" src="https://avatars.githubusercontent.com/u/15005?v=4&s=117" width="117">](https://github.com/tim-kos) |[<img alt="janko" src="https://avatars.githubusercontent.com/u/795488?v=4&s=117" width="117">](https://github.com/janko) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[ajkachnic](https://github.com/ajkachnic) |[dependabot[bot]](https://github.com/apps/dependabot) |[zcallan](https://github.com/zcallan) |[tim-kos](https://github.com/tim-kos) |[janko](https://github.com/janko) |[wilkoklak](https://github.com/wilkoklak) |
+[ajkachnic](https://github.com/ajkachnic) |[dependabot[bot]](https://github.com/apps/dependabot) |[zcallan](https://github.com/zcallan) |[aduh95](https://github.com/aduh95) |[tim-kos](https://github.com/tim-kos) |[janko](https://github.com/janko) |
 
 
-[<img alt="oliverpool" src="https://avatars.githubusercontent.com/u/3864879?v=4&s=117" width="117">](https://github.com/oliverpool) |[<img alt="Botz" src="https://avatars.githubusercontent.com/u/2706678?v=4&s=117" width="117">](https://github.com/Botz) |[<img alt="aduh95" src="https://avatars.githubusercontent.com/u/14309773?v=4&s=117" width="117">](https://github.com/aduh95) |[<img alt="mifi" src="https://avatars.githubusercontent.com/u/402547?v=4&s=117" width="117">](https://github.com/mifi) |[<img alt="mcallistertyler" src="https://avatars.githubusercontent.com/u/14939210?v=4&s=117" width="117">](https://github.com/mcallistertyler) |[<img alt="mokutsu-coursera" src="https://avatars.githubusercontent.com/u/65177495?v=4&s=117" width="117">](https://github.com/mokutsu-coursera) |
+[<img alt="wilkoklak" src="https://avatars.githubusercontent.com/u/17553085?v=4&s=117" width="117">](https://github.com/wilkoklak) |[<img alt="mifi" src="https://avatars.githubusercontent.com/u/402547?v=4&s=117" width="117">](https://github.com/mifi) |[<img alt="oliverpool" src="https://avatars.githubusercontent.com/u/3864879?v=4&s=117" width="117">](https://github.com/oliverpool) |[<img alt="Botz" src="https://avatars.githubusercontent.com/u/2706678?v=4&s=117" width="117">](https://github.com/Botz) |[<img alt="mcallistertyler" src="https://avatars.githubusercontent.com/u/14939210?v=4&s=117" width="117">](https://github.com/mcallistertyler) |[<img alt="mokutsu-coursera" src="https://avatars.githubusercontent.com/u/65177495?v=4&s=117" width="117">](https://github.com/mokutsu-coursera) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[oliverpool](https://github.com/oliverpool) |[Botz](https://github.com/Botz) |[aduh95](https://github.com/aduh95) |[mifi](https://github.com/mifi) |[mcallistertyler](https://github.com/mcallistertyler) |[mokutsu-coursera](https://github.com/mokutsu-coursera) |
+[wilkoklak](https://github.com/wilkoklak) |[mifi](https://github.com/mifi) |[oliverpool](https://github.com/oliverpool) |[Botz](https://github.com/Botz) |[mcallistertyler](https://github.com/mcallistertyler) |[mokutsu-coursera](https://github.com/mokutsu-coursera) |
 
 
 [<img alt="DJWassink" src="https://avatars.githubusercontent.com/u/1822404?v=4&s=117" width="117">](https://github.com/DJWassink) |[<img alt="taoqf" src="https://avatars.githubusercontent.com/u/15901911?v=4&s=117" width="117">](https://github.com/taoqf) |[<img alt="Murderlon" src="https://avatars.githubusercontent.com/u/9060226?v=4&s=117" width="117">](https://github.com/Murderlon) |[<img alt="tuoxiansp" src="https://avatars.githubusercontent.com/u/3960056?v=4&s=117" width="117">](https://github.com/tuoxiansp) |[<img alt="dominiceden" src="https://avatars.githubusercontent.com/u/6367692?v=4&s=117" width="117">](https://github.com/dominiceden) |[<img alt="elenalape" src="https://avatars.githubusercontent.com/u/22844059?v=4&s=117" width="117">](https://github.com/elenalape) |
 [<img alt="DJWassink" src="https://avatars.githubusercontent.com/u/1822404?v=4&s=117" width="117">](https://github.com/DJWassink) |[<img alt="taoqf" src="https://avatars.githubusercontent.com/u/15901911?v=4&s=117" width="117">](https://github.com/taoqf) |[<img alt="Murderlon" src="https://avatars.githubusercontent.com/u/9060226?v=4&s=117" width="117">](https://github.com/Murderlon) |[<img alt="tuoxiansp" src="https://avatars.githubusercontent.com/u/3960056?v=4&s=117" width="117">](https://github.com/tuoxiansp) |[<img alt="dominiceden" src="https://avatars.githubusercontent.com/u/6367692?v=4&s=117" width="117">](https://github.com/dominiceden) |[<img alt="elenalape" src="https://avatars.githubusercontent.com/u/22844059?v=4&s=117" width="117">](https://github.com/elenalape) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
 [DJWassink](https://github.com/DJWassink) |[taoqf](https://github.com/taoqf) |[Murderlon](https://github.com/Murderlon) |[tuoxiansp](https://github.com/tuoxiansp) |[dominiceden](https://github.com/dominiceden) |[elenalape](https://github.com/elenalape) |
 [DJWassink](https://github.com/DJWassink) |[taoqf](https://github.com/taoqf) |[Murderlon](https://github.com/Murderlon) |[tuoxiansp](https://github.com/tuoxiansp) |[dominiceden](https://github.com/dominiceden) |[elenalape](https://github.com/elenalape) |
 
 
-[<img alt="gavboulton" src="https://avatars.githubusercontent.com/u/3900826?v=4&s=117" width="117">](https://github.com/gavboulton) |[<img alt="bertho-zero" src="https://avatars.githubusercontent.com/u/8525267?v=4&s=117" width="117">](https://github.com/bertho-zero) |[<img alt="tranvansang" src="https://avatars.githubusercontent.com/u/13043196?v=4&s=117" width="117">](https://github.com/tranvansang) |[<img alt="ap--" src="https://avatars.githubusercontent.com/u/1463443?v=4&s=117" width="117">](https://github.com/ap--) |[<img alt="mrbatista" src="https://avatars.githubusercontent.com/u/6544817?v=4&s=117" width="117">](https://github.com/mrbatista) |[<img alt="MikeKovarik" src="https://avatars.githubusercontent.com/u/3995401?v=4&s=117" width="117">](https://github.com/MikeKovarik) |
+[<img alt="mejiaej" src="https://avatars.githubusercontent.com/u/4699893?v=4&s=117" width="117">](https://github.com/mejiaej) |[<img alt="gavboulton" src="https://avatars.githubusercontent.com/u/3900826?v=4&s=117" width="117">](https://github.com/gavboulton) |[<img alt="bertho-zero" src="https://avatars.githubusercontent.com/u/8525267?v=4&s=117" width="117">](https://github.com/bertho-zero) |[<img alt="tranvansang" src="https://avatars.githubusercontent.com/u/13043196?v=4&s=117" width="117">](https://github.com/tranvansang) |[<img alt="ap--" src="https://avatars.githubusercontent.com/u/1463443?v=4&s=117" width="117">](https://github.com/ap--) |[<img alt="mrbatista" src="https://avatars.githubusercontent.com/u/6544817?v=4&s=117" width="117">](https://github.com/mrbatista) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[gavboulton](https://github.com/gavboulton) |[bertho-zero](https://github.com/bertho-zero) |[tranvansang](https://github.com/tranvansang) |[ap--](https://github.com/ap--) |[mrbatista](https://github.com/mrbatista) |[MikeKovarik](https://github.com/MikeKovarik) |
+[mejiaej](https://github.com/mejiaej) |[gavboulton](https://github.com/gavboulton) |[bertho-zero](https://github.com/bertho-zero) |[tranvansang](https://github.com/tranvansang) |[ap--](https://github.com/ap--) |[mrbatista](https://github.com/mrbatista) |
 
 
-[<img alt="pauln" src="https://avatars.githubusercontent.com/u/574359?v=4&s=117" width="117">](https://github.com/pauln) |[<img alt="szh" src="https://avatars.githubusercontent.com/u/546965?v=4&s=117" width="117">](https://github.com/szh) |[<img alt="toadkicker" src="https://avatars.githubusercontent.com/u/523330?v=4&s=117" width="117">](https://github.com/toadkicker) |[<img alt="ofhope" src="https://avatars.githubusercontent.com/u/1826459?v=4&s=117" width="117">](https://github.com/ofhope) |[<img alt="mejiaej" src="https://avatars.githubusercontent.com/u/4699893?v=4&s=117" width="117">](https://github.com/mejiaej) |[<img alt="johnnyperkins" src="https://avatars.githubusercontent.com/u/16482282?v=4&s=117" width="117">](https://github.com/johnnyperkins) |
+[<img alt="MikeKovarik" src="https://avatars.githubusercontent.com/u/3995401?v=4&s=117" width="117">](https://github.com/MikeKovarik) |[<img alt="pauln" src="https://avatars.githubusercontent.com/u/574359?v=4&s=117" width="117">](https://github.com/pauln) |[<img alt="szh" src="https://avatars.githubusercontent.com/u/546965?v=4&s=117" width="117">](https://github.com/szh) |[<img alt="toadkicker" src="https://avatars.githubusercontent.com/u/523330?v=4&s=117" width="117">](https://github.com/toadkicker) |[<img alt="ofhope" src="https://avatars.githubusercontent.com/u/1826459?v=4&s=117" width="117">](https://github.com/ofhope) |[<img alt="johnnyperkins" src="https://avatars.githubusercontent.com/u/16482282?v=4&s=117" width="117">](https://github.com/johnnyperkins) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[pauln](https://github.com/pauln) |[szh](https://github.com/szh) |[toadkicker](https://github.com/toadkicker) |[ofhope](https://github.com/ofhope) |[mejiaej](https://github.com/mejiaej) |[johnnyperkins](https://github.com/johnnyperkins) |
+[MikeKovarik](https://github.com/MikeKovarik) |[pauln](https://github.com/pauln) |[szh](https://github.com/szh) |[toadkicker](https://github.com/toadkicker) |[ofhope](https://github.com/ofhope) |[johnnyperkins](https://github.com/johnnyperkins) |
 
 
 [<img alt="dargmuesli" src="https://avatars.githubusercontent.com/u/4778485?v=4&s=117" width="117">](https://github.com/dargmuesli) |[<img alt="manuelkiessling" src="https://avatars.githubusercontent.com/u/206592?v=4&s=117" width="117">](https://github.com/manuelkiessling) |[<img alt="nndevstudio" src="https://avatars.githubusercontent.com/u/22050968?v=4&s=117" width="117">](https://github.com/nndevstudio) |[<img alt="ogtfaber" src="https://avatars.githubusercontent.com/u/320955?v=4&s=117" width="117">](https://github.com/ogtfaber) |[<img alt="sksavant" src="https://avatars.githubusercontent.com/u/1040701?v=4&s=117" width="117">](https://github.com/sksavant) |[<img alt="suchoproduction" src="https://avatars.githubusercontent.com/u/6931349?v=4&s=117" width="117">](https://github.com/suchoproduction) |
 [<img alt="dargmuesli" src="https://avatars.githubusercontent.com/u/4778485?v=4&s=117" width="117">](https://github.com/dargmuesli) |[<img alt="manuelkiessling" src="https://avatars.githubusercontent.com/u/206592?v=4&s=117" width="117">](https://github.com/manuelkiessling) |[<img alt="nndevstudio" src="https://avatars.githubusercontent.com/u/22050968?v=4&s=117" width="117">](https://github.com/nndevstudio) |[<img alt="ogtfaber" src="https://avatars.githubusercontent.com/u/320955?v=4&s=117" width="117">](https://github.com/ogtfaber) |[<img alt="sksavant" src="https://avatars.githubusercontent.com/u/1040701?v=4&s=117" width="117">](https://github.com/sksavant) |[<img alt="suchoproduction" src="https://avatars.githubusercontent.com/u/6931349?v=4&s=117" width="117">](https://github.com/suchoproduction) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
@@ -292,145 +291,145 @@ Use Uppy in your project? [Let us know](https://github.com/transloadit/uppy/issu
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
 [DenysNosov](https://github.com/DenysNosov) |[ethanwillis](https://github.com/ethanwillis) |[frobinsonj](https://github.com/frobinsonj) |[geertclerx](https://github.com/geertclerx) |[jasonbosco](https://github.com/jasonbosco) |[jedwood](https://github.com/jedwood) |
 [DenysNosov](https://github.com/DenysNosov) |[ethanwillis](https://github.com/ethanwillis) |[frobinsonj](https://github.com/frobinsonj) |[geertclerx](https://github.com/geertclerx) |[jasonbosco](https://github.com/jasonbosco) |[jedwood](https://github.com/jedwood) |
 
 
-[<img alt="dogrocker" src="https://avatars.githubusercontent.com/u/8379027?v=4&s=117" width="117">](https://github.com/dogrocker) |[<img alt="lamartire" src="https://avatars.githubusercontent.com/u/13414205?v=4&s=117" width="117">](https://github.com/lamartire) |[<img alt="Mactaivsh" src="https://avatars.githubusercontent.com/u/12948083?v=4&s=117" width="117">](https://github.com/Mactaivsh) |[<img alt="maferland" src="https://avatars.githubusercontent.com/u/5889721?v=4&s=117" width="117">](https://github.com/maferland) |[<img alt="Martin005" src="https://avatars.githubusercontent.com/u/10096404?v=4&s=117" width="117">](https://github.com/Martin005) |[<img alt="martiuslim" src="https://avatars.githubusercontent.com/u/17944339?v=4&s=117" width="117">](https://github.com/martiuslim) |
+[<img alt="juliangruber" src="https://avatars.githubusercontent.com/u/10247?v=4&s=117" width="117">](https://github.com/juliangruber) |[<img alt="dogrocker" src="https://avatars.githubusercontent.com/u/8379027?v=4&s=117" width="117">](https://github.com/dogrocker) |[<img alt="lamartire" src="https://avatars.githubusercontent.com/u/13414205?v=4&s=117" width="117">](https://github.com/lamartire) |[<img alt="Mactaivsh" src="https://avatars.githubusercontent.com/u/12948083?v=4&s=117" width="117">](https://github.com/Mactaivsh) |[<img alt="maferland" src="https://avatars.githubusercontent.com/u/5889721?v=4&s=117" width="117">](https://github.com/maferland) |[<img alt="Martin005" src="https://avatars.githubusercontent.com/u/10096404?v=4&s=117" width="117">](https://github.com/Martin005) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[dogrocker](https://github.com/dogrocker) |[lamartire](https://github.com/lamartire) |[Mactaivsh](https://github.com/Mactaivsh) |[maferland](https://github.com/maferland) |[Martin005](https://github.com/Martin005) |[martiuslim](https://github.com/martiuslim) |
+[juliangruber](https://github.com/juliangruber) |[dogrocker](https://github.com/dogrocker) |[lamartire](https://github.com/lamartire) |[Mactaivsh](https://github.com/Mactaivsh) |[maferland](https://github.com/maferland) |[Martin005](https://github.com/Martin005) |
 
 
-[<img alt="MatthiasKunnen" src="https://avatars.githubusercontent.com/u/16807587?v=4&s=117" width="117">](https://github.com/MatthiasKunnen) |[<img alt="msand" src="https://avatars.githubusercontent.com/u/1131362?v=4&s=117" width="117">](https://github.com/msand) |[<img alt="richartkeil" src="https://avatars.githubusercontent.com/u/8680858?v=4&s=117" width="117">](https://github.com/richartkeil) |[<img alt="richmeij" src="https://avatars.githubusercontent.com/u/9741858?v=4&s=117" width="117">](https://github.com/richmeij) |[<img alt="rosenfeld" src="https://avatars.githubusercontent.com/u/32246?v=4&s=117" width="117">](https://github.com/rosenfeld) |[<img alt="jrschumacher" src="https://avatars.githubusercontent.com/u/46549?v=4&s=117" width="117">](https://github.com/jrschumacher) |
+[<img alt="martiuslim" src="https://avatars.githubusercontent.com/u/17944339?v=4&s=117" width="117">](https://github.com/martiuslim) |[<img alt="MatthiasKunnen" src="https://avatars.githubusercontent.com/u/16807587?v=4&s=117" width="117">](https://github.com/MatthiasKunnen) |[<img alt="msand" src="https://avatars.githubusercontent.com/u/1131362?v=4&s=117" width="117">](https://github.com/msand) |[<img alt="richartkeil" src="https://avatars.githubusercontent.com/u/8680858?v=4&s=117" width="117">](https://github.com/richartkeil) |[<img alt="richmeij" src="https://avatars.githubusercontent.com/u/9741858?v=4&s=117" width="117">](https://github.com/richmeij) |[<img alt="rosenfeld" src="https://avatars.githubusercontent.com/u/32246?v=4&s=117" width="117">](https://github.com/rosenfeld) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[MatthiasKunnen](https://github.com/MatthiasKunnen) |[msand](https://github.com/msand) |[richartkeil](https://github.com/richartkeil) |[richmeij](https://github.com/richmeij) |[rosenfeld](https://github.com/rosenfeld) |[jrschumacher](https://github.com/jrschumacher) |
+[martiuslim](https://github.com/martiuslim) |[MatthiasKunnen](https://github.com/MatthiasKunnen) |[msand](https://github.com/msand) |[richartkeil](https://github.com/richartkeil) |[richmeij](https://github.com/richmeij) |[rosenfeld](https://github.com/rosenfeld) |
 
 
-[<img alt="ThomasG77" src="https://avatars.githubusercontent.com/u/642120?v=4&s=117" width="117">](https://github.com/ThomasG77) |[<img alt="sparanoid" src="https://avatars.githubusercontent.com/u/96356?v=4&s=117" width="117">](https://github.com/sparanoid) |[<img alt="zhuangya" src="https://avatars.githubusercontent.com/u/499038?v=4&s=117" width="117">](https://github.com/zhuangya) |[<img alt="allenfantasy" src="https://avatars.githubusercontent.com/u/1009294?v=4&s=117" width="117">](https://github.com/allenfantasy) |[<img alt="Zyclotrop-j" src="https://avatars.githubusercontent.com/u/4939546?v=4&s=117" width="117">](https://github.com/Zyclotrop-j) |[<img alt="fortrieb" src="https://avatars.githubusercontent.com/u/4126707?v=4&s=117" width="117">](https://github.com/fortrieb) |
+[<img alt="jrschumacher" src="https://avatars.githubusercontent.com/u/46549?v=4&s=117" width="117">](https://github.com/jrschumacher) |[<img alt="ThomasG77" src="https://avatars.githubusercontent.com/u/642120?v=4&s=117" width="117">](https://github.com/ThomasG77) |[<img alt="sparanoid" src="https://avatars.githubusercontent.com/u/96356?v=4&s=117" width="117">](https://github.com/sparanoid) |[<img alt="zhuangya" src="https://avatars.githubusercontent.com/u/499038?v=4&s=117" width="117">](https://github.com/zhuangya) |[<img alt="allenfantasy" src="https://avatars.githubusercontent.com/u/1009294?v=4&s=117" width="117">](https://github.com/allenfantasy) |[<img alt="Zyclotrop-j" src="https://avatars.githubusercontent.com/u/4939546?v=4&s=117" width="117">](https://github.com/Zyclotrop-j) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[ThomasG77](https://github.com/ThomasG77) |[sparanoid](https://github.com/sparanoid) |[zhuangya](https://github.com/zhuangya) |[allenfantasy](https://github.com/allenfantasy) |[Zyclotrop-j](https://github.com/Zyclotrop-j) |[fortrieb](https://github.com/fortrieb) |
+[jrschumacher](https://github.com/jrschumacher) |[ThomasG77](https://github.com/ThomasG77) |[sparanoid](https://github.com/sparanoid) |[zhuangya](https://github.com/zhuangya) |[allenfantasy](https://github.com/allenfantasy) |[Zyclotrop-j](https://github.com/Zyclotrop-j) |
 
 
-[<img alt="jarey" src="https://avatars.githubusercontent.com/u/5025224?v=4&s=117" width="117">](https://github.com/jarey) |[<img alt="muhammadInam" src="https://avatars.githubusercontent.com/u/7801708?v=4&s=117" width="117">](https://github.com/muhammadInam) |[<img alt="rettgerst" src="https://avatars.githubusercontent.com/u/11684948?v=4&s=117" width="117">](https://github.com/rettgerst) |[<img alt="mkabatek" src="https://avatars.githubusercontent.com/u/1764486?v=4&s=117" width="117">](https://github.com/mkabatek) |[<img alt="jukakoski" src="https://avatars.githubusercontent.com/u/52720967?v=4&s=117" width="117">](https://github.com/jukakoski) |[<img alt="olemoign" src="https://avatars.githubusercontent.com/u/11632871?v=4&s=117" width="117">](https://github.com/olemoign) |
+[<img alt="fortrieb" src="https://avatars.githubusercontent.com/u/4126707?v=4&s=117" width="117">](https://github.com/fortrieb) |[<img alt="jarey" src="https://avatars.githubusercontent.com/u/5025224?v=4&s=117" width="117">](https://github.com/jarey) |[<img alt="muhammadInam" src="https://avatars.githubusercontent.com/u/7801708?v=4&s=117" width="117">](https://github.com/muhammadInam) |[<img alt="rettgerst" src="https://avatars.githubusercontent.com/u/11684948?v=4&s=117" width="117">](https://github.com/rettgerst) |[<img alt="mkabatek" src="https://avatars.githubusercontent.com/u/1764486?v=4&s=117" width="117">](https://github.com/mkabatek) |[<img alt="jukakoski" src="https://avatars.githubusercontent.com/u/52720967?v=4&s=117" width="117">](https://github.com/jukakoski) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[jarey](https://github.com/jarey) |[muhammadInam](https://github.com/muhammadInam) |[rettgerst](https://github.com/rettgerst) |[mkabatek](https://github.com/mkabatek) |[jukakoski](https://github.com/jukakoski) |[olemoign](https://github.com/olemoign) |
+[fortrieb](https://github.com/fortrieb) |[jarey](https://github.com/jarey) |[muhammadInam](https://github.com/muhammadInam) |[rettgerst](https://github.com/rettgerst) |[mkabatek](https://github.com/mkabatek) |[jukakoski](https://github.com/jukakoski) |
 
 
-[<img alt="ajschmidt8" src="https://avatars.githubusercontent.com/u/7400326?v=4&s=117" width="117">](https://github.com/ajschmidt8) |[<img alt="superhawk610" src="https://avatars.githubusercontent.com/u/18172185?v=4&s=117" width="117">](https://github.com/superhawk610) |[<img alt="abannach" src="https://avatars.githubusercontent.com/u/43150303?v=4&s=117" width="117">](https://github.com/abannach) |[<img alt="adamelmore" src="https://avatars.githubusercontent.com/u/2363879?v=4&s=117" width="117">](https://github.com/adamelmore) |[<img alt="ajh-sr" src="https://avatars.githubusercontent.com/u/71472057?v=4&s=117" width="117">](https://github.com/ajh-sr) |[<img alt="adamvigneault" src="https://avatars.githubusercontent.com/u/18236120?v=4&s=117" width="117">](https://github.com/adamvigneault) |
+[<img alt="olemoign" src="https://avatars.githubusercontent.com/u/11632871?v=4&s=117" width="117">](https://github.com/olemoign) |[<img alt="ajschmidt8" src="https://avatars.githubusercontent.com/u/7400326?v=4&s=117" width="117">](https://github.com/ajschmidt8) |[<img alt="superhawk610" src="https://avatars.githubusercontent.com/u/18172185?v=4&s=117" width="117">](https://github.com/superhawk610) |[<img alt="abannach" src="https://avatars.githubusercontent.com/u/43150303?v=4&s=117" width="117">](https://github.com/abannach) |[<img alt="adamelmore" src="https://avatars.githubusercontent.com/u/2363879?v=4&s=117" width="117">](https://github.com/adamelmore) |[<img alt="ajh-sr" src="https://avatars.githubusercontent.com/u/71472057?v=4&s=117" width="117">](https://github.com/ajh-sr) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[ajschmidt8](https://github.com/ajschmidt8) |[superhawk610](https://github.com/superhawk610) |[abannach](https://github.com/abannach) |[adamelmore](https://github.com/adamelmore) |[ajh-sr](https://github.com/ajh-sr) |[adamvigneault](https://github.com/adamvigneault) |
+[olemoign](https://github.com/olemoign) |[ajschmidt8](https://github.com/ajschmidt8) |[superhawk610](https://github.com/superhawk610) |[abannach](https://github.com/abannach) |[adamelmore](https://github.com/adamelmore) |[ajh-sr](https://github.com/ajh-sr) |
 
 
-[<img alt="adritasharma" src="https://avatars.githubusercontent.com/u/29271635?v=4&s=117" width="117">](https://github.com/adritasharma) |[<img alt="asmt3" src="https://avatars.githubusercontent.com/u/1777709?v=4&s=117" width="117">](https://github.com/asmt3) |[<img alt="alexnj" src="https://avatars.githubusercontent.com/u/683500?v=4&s=117" width="117">](https://github.com/alexnj) |[<img alt="aalepis" src="https://avatars.githubusercontent.com/u/35684834?v=4&s=117" width="117">](https://github.com/aalepis) |[<img alt="Dogfalo" src="https://avatars.githubusercontent.com/u/2775751?v=4&s=117" width="117">](https://github.com/Dogfalo) |[<img alt="tekacs" src="https://avatars.githubusercontent.com/u/63247?v=4&s=117" width="117">](https://github.com/tekacs) |
+[<img alt="adamvigneault" src="https://avatars.githubusercontent.com/u/18236120?v=4&s=117" width="117">](https://github.com/adamvigneault) |[<img alt="adritasharma" src="https://avatars.githubusercontent.com/u/29271635?v=4&s=117" width="117">](https://github.com/adritasharma) |[<img alt="asmt3" src="https://avatars.githubusercontent.com/u/1777709?v=4&s=117" width="117">](https://github.com/asmt3) |[<img alt="alexnj" src="https://avatars.githubusercontent.com/u/683500?v=4&s=117" width="117">](https://github.com/alexnj) |[<img alt="aalepis" src="https://avatars.githubusercontent.com/u/35684834?v=4&s=117" width="117">](https://github.com/aalepis) |[<img alt="Dogfalo" src="https://avatars.githubusercontent.com/u/2775751?v=4&s=117" width="117">](https://github.com/Dogfalo) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[adritasharma](https://github.com/adritasharma) |[asmt3](https://github.com/asmt3) |[alexnj](https://github.com/alexnj) |[aalepis](https://github.com/aalepis) |[Dogfalo](https://github.com/Dogfalo) |[tekacs](https://github.com/tekacs) |
+[adamvigneault](https://github.com/adamvigneault) |[adritasharma](https://github.com/adritasharma) |[asmt3](https://github.com/asmt3) |[alexnj](https://github.com/alexnj) |[aalepis](https://github.com/aalepis) |[Dogfalo](https://github.com/Dogfalo) |
 
 
-[<img alt="amitport" src="https://avatars.githubusercontent.com/u/1131991?v=4&s=117" width="117">](https://github.com/amitport) |[<img alt="functino" src="https://avatars.githubusercontent.com/u/415498?v=4&s=117" width="117">](https://github.com/functino) |[<img alt="radarhere" src="https://avatars.githubusercontent.com/u/3112309?v=4&s=117" width="117">](https://github.com/radarhere) |[<img alt="superandrew213" src="https://avatars.githubusercontent.com/u/13059204?v=4&s=117" width="117">](https://github.com/superandrew213) |[<img alt="andychongyz" src="https://avatars.githubusercontent.com/u/12697240?v=4&s=117" width="117">](https://github.com/andychongyz) |[<img alt="anthony0030" src="https://avatars.githubusercontent.com/u/13033263?v=4&s=117" width="117">](https://github.com/anthony0030) |
+[<img alt="tekacs" src="https://avatars.githubusercontent.com/u/63247?v=4&s=117" width="117">](https://github.com/tekacs) |[<img alt="amitport" src="https://avatars.githubusercontent.com/u/1131991?v=4&s=117" width="117">](https://github.com/amitport) |[<img alt="functino" src="https://avatars.githubusercontent.com/u/415498?v=4&s=117" width="117">](https://github.com/functino) |[<img alt="radarhere" src="https://avatars.githubusercontent.com/u/3112309?v=4&s=117" width="117">](https://github.com/radarhere) |[<img alt="superandrew213" src="https://avatars.githubusercontent.com/u/13059204?v=4&s=117" width="117">](https://github.com/superandrew213) |[<img alt="andychongyz" src="https://avatars.githubusercontent.com/u/12697240?v=4&s=117" width="117">](https://github.com/andychongyz) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[amitport](https://github.com/amitport) |[functino](https://github.com/functino) |[radarhere](https://github.com/radarhere) |[superandrew213](https://github.com/superandrew213) |[andychongyz](https://github.com/andychongyz) |[anthony0030](https://github.com/anthony0030) |
+[tekacs](https://github.com/tekacs) |[amitport](https://github.com/amitport) |[functino](https://github.com/functino) |[radarhere](https://github.com/radarhere) |[superandrew213](https://github.com/superandrew213) |[andychongyz](https://github.com/andychongyz) |
 
 
-[<img alt="Abourass" src="https://avatars.githubusercontent.com/u/39917231?v=4&s=117" width="117">](https://github.com/Abourass) |[<img alt="arthurdenner" src="https://avatars.githubusercontent.com/u/13774309?v=4&s=117" width="117">](https://github.com/arthurdenner) |[<img alt="apuyou" src="https://avatars.githubusercontent.com/u/520053?v=4&s=117" width="117">](https://github.com/apuyou) |[<img alt="atsawin" src="https://avatars.githubusercontent.com/u/666663?v=4&s=117" width="117">](https://github.com/atsawin) |[<img alt="ayhankesicioglu" src="https://avatars.githubusercontent.com/u/36304312?v=4&s=117" width="117">](https://github.com/ayhankesicioglu) |[<img alt="azeemba" src="https://avatars.githubusercontent.com/u/2160795?v=4&s=117" width="117">](https://github.com/azeemba) |
+[<img alt="anthony0030" src="https://avatars.githubusercontent.com/u/13033263?v=4&s=117" width="117">](https://github.com/anthony0030) |[<img alt="Abourass" src="https://avatars.githubusercontent.com/u/39917231?v=4&s=117" width="117">](https://github.com/Abourass) |[<img alt="arthurdenner" src="https://avatars.githubusercontent.com/u/13774309?v=4&s=117" width="117">](https://github.com/arthurdenner) |[<img alt="apuyou" src="https://avatars.githubusercontent.com/u/520053?v=4&s=117" width="117">](https://github.com/apuyou) |[<img alt="atsawin" src="https://avatars.githubusercontent.com/u/666663?v=4&s=117" width="117">](https://github.com/atsawin) |[<img alt="ayhankesicioglu" src="https://avatars.githubusercontent.com/u/36304312?v=4&s=117" width="117">](https://github.com/ayhankesicioglu) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[Abourass](https://github.com/Abourass) |[arthurdenner](https://github.com/arthurdenner) |[apuyou](https://github.com/apuyou) |[atsawin](https://github.com/atsawin) |[ayhankesicioglu](https://github.com/ayhankesicioglu) |[azeemba](https://github.com/azeemba) |
+[anthony0030](https://github.com/anthony0030) |[Abourass](https://github.com/Abourass) |[arthurdenner](https://github.com/arthurdenner) |[apuyou](https://github.com/apuyou) |[atsawin](https://github.com/atsawin) |[ayhankesicioglu](https://github.com/ayhankesicioglu) |
 
 
-[<img alt="azizk" src="https://avatars.githubusercontent.com/u/37282?v=4&s=117" width="117">](https://github.com/azizk) |[<img alt="bducharme" src="https://avatars.githubusercontent.com/u/4173569?v=4&s=117" width="117">](https://github.com/bducharme) |[<img alt="Quorafind" src="https://avatars.githubusercontent.com/u/13215013?v=4&s=117" width="117">](https://github.com/Quorafind) |[<img alt="wbaaron" src="https://avatars.githubusercontent.com/u/1048988?v=4&s=117" width="117">](https://github.com/wbaaron) |[<img alt="bedgerotto" src="https://avatars.githubusercontent.com/u/4459657?v=4&s=117" width="117">](https://github.com/bedgerotto) |[<img alt="cyu" src="https://avatars.githubusercontent.com/u/2431?v=4&s=117" width="117">](https://github.com/cyu) |
+[<img alt="azeemba" src="https://avatars.githubusercontent.com/u/2160795?v=4&s=117" width="117">](https://github.com/azeemba) |[<img alt="azizk" src="https://avatars.githubusercontent.com/u/37282?v=4&s=117" width="117">](https://github.com/azizk) |[<img alt="bducharme" src="https://avatars.githubusercontent.com/u/4173569?v=4&s=117" width="117">](https://github.com/bducharme) |[<img alt="Quorafind" src="https://avatars.githubusercontent.com/u/13215013?v=4&s=117" width="117">](https://github.com/Quorafind) |[<img alt="wbaaron" src="https://avatars.githubusercontent.com/u/1048988?v=4&s=117" width="117">](https://github.com/wbaaron) |[<img alt="bedgerotto" src="https://avatars.githubusercontent.com/u/4459657?v=4&s=117" width="117">](https://github.com/bedgerotto) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[azizk](https://github.com/azizk) |[bducharme](https://github.com/bducharme) |[Quorafind](https://github.com/Quorafind) |[wbaaron](https://github.com/wbaaron) |[bedgerotto](https://github.com/bedgerotto) |[cyu](https://github.com/cyu) |
+[azeemba](https://github.com/azeemba) |[azizk](https://github.com/azizk) |[bducharme](https://github.com/bducharme) |[Quorafind](https://github.com/Quorafind) |[wbaaron](https://github.com/wbaaron) |[bedgerotto](https://github.com/bedgerotto) |
 
 
-[<img alt="cartfisk" src="https://avatars.githubusercontent.com/u/8764375?v=4&s=117" width="117">](https://github.com/cartfisk) |[<img alt="cellvinchung" src="https://avatars.githubusercontent.com/u/5347394?v=4&s=117" width="117">](https://github.com/cellvinchung) |[<img alt="chao" src="https://avatars.githubusercontent.com/u/55872?v=4&s=117" width="117">](https://github.com/chao) |[<img alt="csprance" src="https://avatars.githubusercontent.com/u/7902617?v=4&s=117" width="117">](https://github.com/csprance) |[<img alt="Aarbel" src="https://avatars.githubusercontent.com/u/25119847?v=4&s=117" width="117">](https://github.com/Aarbel) |[<img alt="cbush06" src="https://avatars.githubusercontent.com/u/15720146?v=4&s=117" width="117">](https://github.com/cbush06) |
+[<img alt="cyu" src="https://avatars.githubusercontent.com/u/2431?v=4&s=117" width="117">](https://github.com/cyu) |[<img alt="cartfisk" src="https://avatars.githubusercontent.com/u/8764375?v=4&s=117" width="117">](https://github.com/cartfisk) |[<img alt="cellvinchung" src="https://avatars.githubusercontent.com/u/5347394?v=4&s=117" width="117">](https://github.com/cellvinchung) |[<img alt="chao" src="https://avatars.githubusercontent.com/u/55872?v=4&s=117" width="117">](https://github.com/chao) |[<img alt="csprance" src="https://avatars.githubusercontent.com/u/7902617?v=4&s=117" width="117">](https://github.com/csprance) |[<img alt="Aarbel" src="https://avatars.githubusercontent.com/u/25119847?v=4&s=117" width="117">](https://github.com/Aarbel) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[cartfisk](https://github.com/cartfisk) |[cellvinchung](https://github.com/cellvinchung) |[chao](https://github.com/chao) |[csprance](https://github.com/csprance) |[Aarbel](https://github.com/Aarbel) |[cbush06](https://github.com/cbush06) |
+[cyu](https://github.com/cyu) |[cartfisk](https://github.com/cartfisk) |[cellvinchung](https://github.com/cellvinchung) |[chao](https://github.com/chao) |[csprance](https://github.com/csprance) |[Aarbel](https://github.com/Aarbel) |
 
 
-[<img alt="czj" src="https://avatars.githubusercontent.com/u/14306?v=4&s=117" width="117">](https://github.com/czj) |[<img alt="ardeois" src="https://avatars.githubusercontent.com/u/1867939?v=4&s=117" width="117">](https://github.com/ardeois) |[<img alt="sercraig" src="https://avatars.githubusercontent.com/u/24261518?v=4&s=117" width="117">](https://github.com/sercraig) |[<img alt="Cruaier" src="https://avatars.githubusercontent.com/u/5204940?v=4&s=117" width="117">](https://github.com/Cruaier) |[<img alt="danmichaelo" src="https://avatars.githubusercontent.com/u/434495?v=4&s=117" width="117">](https://github.com/danmichaelo) |[<img alt="mrboomer" src="https://avatars.githubusercontent.com/u/5942912?v=4&s=117" width="117">](https://github.com/mrboomer) |
+[<img alt="cbush06" src="https://avatars.githubusercontent.com/u/15720146?v=4&s=117" width="117">](https://github.com/cbush06) |[<img alt="czj" src="https://avatars.githubusercontent.com/u/14306?v=4&s=117" width="117">](https://github.com/czj) |[<img alt="ardeois" src="https://avatars.githubusercontent.com/u/1867939?v=4&s=117" width="117">](https://github.com/ardeois) |[<img alt="sercraig" src="https://avatars.githubusercontent.com/u/24261518?v=4&s=117" width="117">](https://github.com/sercraig) |[<img alt="Cruaier" src="https://avatars.githubusercontent.com/u/5204940?v=4&s=117" width="117">](https://github.com/Cruaier) |[<img alt="danmichaelo" src="https://avatars.githubusercontent.com/u/434495?v=4&s=117" width="117">](https://github.com/danmichaelo) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[czj](https://github.com/czj) |[ardeois](https://github.com/ardeois) |[sercraig](https://github.com/sercraig) |[Cruaier](https://github.com/Cruaier) |[danmichaelo](https://github.com/danmichaelo) |[mrboomer](https://github.com/mrboomer) |
+[cbush06](https://github.com/cbush06) |[czj](https://github.com/czj) |[ardeois](https://github.com/ardeois) |[sercraig](https://github.com/sercraig) |[Cruaier](https://github.com/Cruaier) |[danmichaelo](https://github.com/danmichaelo) |
 
 
-[<img alt="akizor" src="https://avatars.githubusercontent.com/u/1052439?v=4&s=117" width="117">](https://github.com/akizor) |[<img alt="davilima6" src="https://avatars.githubusercontent.com/u/422130?v=4&s=117" width="117">](https://github.com/davilima6) |[<img alt="DennisKofflard" src="https://avatars.githubusercontent.com/u/8669129?v=4&s=117" width="117">](https://github.com/DennisKofflard) |[<img alt="jeetiss" src="https://avatars.githubusercontent.com/u/6726016?v=4&s=117" width="117">](https://github.com/jeetiss) |[<img alt="sweetro" src="https://avatars.githubusercontent.com/u/6228717?v=4&s=117" width="117">](https://github.com/sweetro) |[<img alt="efbautista" src="https://avatars.githubusercontent.com/u/35430671?v=4&s=117" width="117">](https://github.com/efbautista) |
+[<img alt="mrboomer" src="https://avatars.githubusercontent.com/u/5942912?v=4&s=117" width="117">](https://github.com/mrboomer) |[<img alt="akizor" src="https://avatars.githubusercontent.com/u/1052439?v=4&s=117" width="117">](https://github.com/akizor) |[<img alt="davilima6" src="https://avatars.githubusercontent.com/u/422130?v=4&s=117" width="117">](https://github.com/davilima6) |[<img alt="DennisKofflard" src="https://avatars.githubusercontent.com/u/8669129?v=4&s=117" width="117">](https://github.com/DennisKofflard) |[<img alt="jeetiss" src="https://avatars.githubusercontent.com/u/6726016?v=4&s=117" width="117">](https://github.com/jeetiss) |[<img alt="sweetro" src="https://avatars.githubusercontent.com/u/6228717?v=4&s=117" width="117">](https://github.com/sweetro) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[akizor](https://github.com/akizor) |[davilima6](https://github.com/davilima6) |[DennisKofflard](https://github.com/DennisKofflard) |[jeetiss](https://github.com/jeetiss) |[sweetro](https://github.com/sweetro) |[efbautista](https://github.com/efbautista) |
+[mrboomer](https://github.com/mrboomer) |[akizor](https://github.com/akizor) |[davilima6](https://github.com/davilima6) |[DennisKofflard](https://github.com/DennisKofflard) |[jeetiss](https://github.com/jeetiss) |[sweetro](https://github.com/sweetro) |
 
 
-[<img alt="yoldar" src="https://avatars.githubusercontent.com/u/1597578?v=4&s=117" width="117">](https://github.com/yoldar) |[<img alt="eliOcs" src="https://avatars.githubusercontent.com/u/1283954?v=4&s=117" width="117">](https://github.com/eliOcs) |[<img alt="EnricoSottile" src="https://avatars.githubusercontent.com/u/10349653?v=4&s=117" width="117">](https://github.com/EnricoSottile) |[<img alt="Gkleinereva" src="https://avatars.githubusercontent.com/u/23621633?v=4&s=117" width="117">](https://github.com/Gkleinereva) |[<img alt="fgallinari" src="https://avatars.githubusercontent.com/u/6473638?v=4&s=117" width="117">](https://github.com/fgallinari) |[<img alt="ferdiusa" src="https://avatars.githubusercontent.com/u/1997982?v=4&s=117" width="117">](https://github.com/ferdiusa) |
+[<img alt="efbautista" src="https://avatars.githubusercontent.com/u/35430671?v=4&s=117" width="117">](https://github.com/efbautista) |[<img alt="yoldar" src="https://avatars.githubusercontent.com/u/1597578?v=4&s=117" width="117">](https://github.com/yoldar) |[<img alt="eliOcs" src="https://avatars.githubusercontent.com/u/1283954?v=4&s=117" width="117">](https://github.com/eliOcs) |[<img alt="EnricoSottile" src="https://avatars.githubusercontent.com/u/10349653?v=4&s=117" width="117">](https://github.com/EnricoSottile) |[<img alt="Gkleinereva" src="https://avatars.githubusercontent.com/u/23621633?v=4&s=117" width="117">](https://github.com/Gkleinereva) |[<img alt="fgallinari" src="https://avatars.githubusercontent.com/u/6473638?v=4&s=117" width="117">](https://github.com/fgallinari) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[yoldar](https://github.com/yoldar) |[eliOcs](https://github.com/eliOcs) |[EnricoSottile](https://github.com/EnricoSottile) |[Gkleinereva](https://github.com/Gkleinereva) |[fgallinari](https://github.com/fgallinari) |[ferdiusa](https://github.com/ferdiusa) |
+[efbautista](https://github.com/efbautista) |[yoldar](https://github.com/yoldar) |[eliOcs](https://github.com/eliOcs) |[EnricoSottile](https://github.com/EnricoSottile) |[Gkleinereva](https://github.com/Gkleinereva) |[fgallinari](https://github.com/fgallinari) |
 
 
-[<img alt="dtrucs" src="https://avatars.githubusercontent.com/u/1926041?v=4&s=117" width="117">](https://github.com/dtrucs) |[<img alt="geoffappleford" src="https://avatars.githubusercontent.com/u/731678?v=4&s=117" width="117">](https://github.com/geoffappleford) |[<img alt="gjungb" src="https://avatars.githubusercontent.com/u/3391068?v=4&s=117" width="117">](https://github.com/gjungb) |[<img alt="roenschg" src="https://avatars.githubusercontent.com/u/9590236?v=4&s=117" width="117">](https://github.com/roenschg) |[<img alt="HughbertD" src="https://avatars.githubusercontent.com/u/1580021?v=4&s=117" width="117">](https://github.com/HughbertD) |[<img alt="HussainAlkhalifah" src="https://avatars.githubusercontent.com/u/43642162?v=4&s=117" width="117">](https://github.com/HussainAlkhalifah) |
+[<img alt="ferdiusa" src="https://avatars.githubusercontent.com/u/1997982?v=4&s=117" width="117">](https://github.com/ferdiusa) |[<img alt="dtrucs" src="https://avatars.githubusercontent.com/u/1926041?v=4&s=117" width="117">](https://github.com/dtrucs) |[<img alt="geoffappleford" src="https://avatars.githubusercontent.com/u/731678?v=4&s=117" width="117">](https://github.com/geoffappleford) |[<img alt="gjungb" src="https://avatars.githubusercontent.com/u/3391068?v=4&s=117" width="117">](https://github.com/gjungb) |[<img alt="roenschg" src="https://avatars.githubusercontent.com/u/9590236?v=4&s=117" width="117">](https://github.com/roenschg) |[<img alt="HughbertD" src="https://avatars.githubusercontent.com/u/1580021?v=4&s=117" width="117">](https://github.com/HughbertD) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[dtrucs](https://github.com/dtrucs) |[geoffappleford](https://github.com/geoffappleford) |[gjungb](https://github.com/gjungb) |[roenschg](https://github.com/roenschg) |[HughbertD](https://github.com/HughbertD) |[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |
+[ferdiusa](https://github.com/ferdiusa) |[dtrucs](https://github.com/dtrucs) |[geoffappleford](https://github.com/geoffappleford) |[gjungb](https://github.com/gjungb) |[roenschg](https://github.com/roenschg) |[HughbertD](https://github.com/HughbertD) |
 
 
-[<img alt="huydod" src="https://avatars.githubusercontent.com/u/37580530?v=4&s=117" width="117">](https://github.com/huydod) |[<img alt="IanVS" src="https://avatars.githubusercontent.com/u/4616705?v=4&s=117" width="117">](https://github.com/IanVS) |[<img alt="ishendyweb" src="https://avatars.githubusercontent.com/u/10582418?v=4&s=117" width="117">](https://github.com/ishendyweb) |[<img alt="NaxYo" src="https://avatars.githubusercontent.com/u/1963876?v=4&s=117" width="117">](https://github.com/NaxYo) |[<img alt="ghasrfakhri" src="https://avatars.githubusercontent.com/u/4945963?v=4&s=117" width="117">](https://github.com/ghasrfakhri) |[<img alt="intenzive" src="https://avatars.githubusercontent.com/u/11055931?v=4&s=117" width="117">](https://github.com/intenzive) |
+[<img alt="HussainAlkhalifah" src="https://avatars.githubusercontent.com/u/43642162?v=4&s=117" width="117">](https://github.com/HussainAlkhalifah) |[<img alt="huydod" src="https://avatars.githubusercontent.com/u/37580530?v=4&s=117" width="117">](https://github.com/huydod) |[<img alt="IanVS" src="https://avatars.githubusercontent.com/u/4616705?v=4&s=117" width="117">](https://github.com/IanVS) |[<img alt="ishendyweb" src="https://avatars.githubusercontent.com/u/10582418?v=4&s=117" width="117">](https://github.com/ishendyweb) |[<img alt="NaxYo" src="https://avatars.githubusercontent.com/u/1963876?v=4&s=117" width="117">](https://github.com/NaxYo) |[<img alt="ghasrfakhri" src="https://avatars.githubusercontent.com/u/4945963?v=4&s=117" width="117">](https://github.com/ghasrfakhri) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[huydod](https://github.com/huydod) |[IanVS](https://github.com/IanVS) |[ishendyweb](https://github.com/ishendyweb) |[NaxYo](https://github.com/NaxYo) |[ghasrfakhri](https://github.com/ghasrfakhri) |[intenzive](https://github.com/intenzive) |
+[HussainAlkhalifah](https://github.com/HussainAlkhalifah) |[huydod](https://github.com/huydod) |[IanVS](https://github.com/IanVS) |[ishendyweb](https://github.com/ishendyweb) |[NaxYo](https://github.com/NaxYo) |[ghasrfakhri](https://github.com/ghasrfakhri) |
 
 
-[<img alt="GreenJimmy" src="https://avatars.githubusercontent.com/u/39386?v=4&s=117" width="117">](https://github.com/GreenJimmy) |[<img alt="mazoruss" src="https://avatars.githubusercontent.com/u/17625190?v=4&s=117" width="117">](https://github.com/mazoruss) |[<img alt="JacobMGEvans" src="https://avatars.githubusercontent.com/u/27247160?v=4&s=117" width="117">](https://github.com/JacobMGEvans) |[<img alt="jdssem" src="https://avatars.githubusercontent.com/u/978944?v=4&s=117" width="117">](https://github.com/jdssem) |[<img alt="Jbithell" src="https://avatars.githubusercontent.com/u/8408967?v=4&s=117" width="117">](https://github.com/Jbithell) |[<img alt="jcjmcclean" src="https://avatars.githubusercontent.com/u/1822574?v=4&s=117" width="117">](https://github.com/jcjmcclean) |
+[<img alt="intenzive" src="https://avatars.githubusercontent.com/u/11055931?v=4&s=117" width="117">](https://github.com/intenzive) |[<img alt="Hawxy" src="https://avatars.githubusercontent.com/u/975824?v=4&s=117" width="117">](https://github.com/Hawxy) |[<img alt="GreenJimmy" src="https://avatars.githubusercontent.com/u/39386?v=4&s=117" width="117">](https://github.com/GreenJimmy) |[<img alt="mazoruss" src="https://avatars.githubusercontent.com/u/17625190?v=4&s=117" width="117">](https://github.com/mazoruss) |[<img alt="JacobMGEvans" src="https://avatars.githubusercontent.com/u/27247160?v=4&s=117" width="117">](https://github.com/JacobMGEvans) |[<img alt="jdssem" src="https://avatars.githubusercontent.com/u/978944?v=4&s=117" width="117">](https://github.com/jdssem) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[GreenJimmy](https://github.com/GreenJimmy) |[mazoruss](https://github.com/mazoruss) |[JacobMGEvans](https://github.com/JacobMGEvans) |[jdssem](https://github.com/jdssem) |[Jbithell](https://github.com/Jbithell) |[jcjmcclean](https://github.com/jcjmcclean) |
+[intenzive](https://github.com/intenzive) |[Hawxy](https://github.com/Hawxy) |[GreenJimmy](https://github.com/GreenJimmy) |[mazoruss](https://github.com/mazoruss) |[JacobMGEvans](https://github.com/JacobMGEvans) |[jdssem](https://github.com/jdssem) |
 
 
-[<img alt="janklimo" src="https://avatars.githubusercontent.com/u/7811733?v=4&s=117" width="117">](https://github.com/janklimo) |[<img alt="janwilts" src="https://avatars.githubusercontent.com/u/16721581?v=4&s=117" width="117">](https://github.com/janwilts) |[<img alt="vith" src="https://avatars.githubusercontent.com/u/3265539?v=4&s=117" width="117">](https://github.com/vith) |[<img alt="jessica-coursera" src="https://avatars.githubusercontent.com/u/35155465?v=4&s=117" width="117">](https://github.com/jessica-coursera) |[<img alt="jhen0409" src="https://avatars.githubusercontent.com/u/3001525?v=4&s=117" width="117">](https://github.com/jhen0409) |[<img alt="Jmales" src="https://avatars.githubusercontent.com/u/22914881?v=4&s=117" width="117">](https://github.com/Jmales) |
+[<img alt="Jbithell" src="https://avatars.githubusercontent.com/u/8408967?v=4&s=117" width="117">](https://github.com/Jbithell) |[<img alt="jcjmcclean" src="https://avatars.githubusercontent.com/u/1822574?v=4&s=117" width="117">](https://github.com/jcjmcclean) |[<img alt="janklimo" src="https://avatars.githubusercontent.com/u/7811733?v=4&s=117" width="117">](https://github.com/janklimo) |[<img alt="janwilts" src="https://avatars.githubusercontent.com/u/16721581?v=4&s=117" width="117">](https://github.com/janwilts) |[<img alt="vith" src="https://avatars.githubusercontent.com/u/3265539?v=4&s=117" width="117">](https://github.com/vith) |[<img alt="jessica-coursera" src="https://avatars.githubusercontent.com/u/35155465?v=4&s=117" width="117">](https://github.com/jessica-coursera) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[janklimo](https://github.com/janklimo) |[janwilts](https://github.com/janwilts) |[vith](https://github.com/vith) |[jessica-coursera](https://github.com/jessica-coursera) |[jhen0409](https://github.com/jhen0409) |[Jmales](https://github.com/Jmales) |
+[Jbithell](https://github.com/Jbithell) |[jcjmcclean](https://github.com/jcjmcclean) |[janklimo](https://github.com/janklimo) |[janwilts](https://github.com/janwilts) |[vith](https://github.com/vith) |[jessica-coursera](https://github.com/jessica-coursera) |
 
 
-[<img alt="theJoeBiz" src="https://avatars.githubusercontent.com/u/189589?v=4&s=117" width="117">](https://github.com/theJoeBiz) |[<img alt="profsmallpine" src="https://avatars.githubusercontent.com/u/7328006?v=4&s=117" width="117">](https://github.com/profsmallpine) |[<img alt="jonathanarbely" src="https://avatars.githubusercontent.com/u/18177203?v=4&s=117" width="117">](https://github.com/jonathanarbely) |[<img alt="jderrough" src="https://avatars.githubusercontent.com/u/1108358?v=4&s=117" width="117">](https://github.com/jderrough) |[<img alt="jonathanly" src="https://avatars.githubusercontent.com/u/13286473?v=4&s=117" width="117">](https://github.com/jonathanly) |[<img alt="jorgeepc" src="https://avatars.githubusercontent.com/u/3879892?v=4&s=117" width="117">](https://github.com/jorgeepc) |
+[<img alt="jhen0409" src="https://avatars.githubusercontent.com/u/3001525?v=4&s=117" width="117">](https://github.com/jhen0409) |[<img alt="Jmales" src="https://avatars.githubusercontent.com/u/22914881?v=4&s=117" width="117">](https://github.com/Jmales) |[<img alt="theJoeBiz" src="https://avatars.githubusercontent.com/u/189589?v=4&s=117" width="117">](https://github.com/theJoeBiz) |[<img alt="profsmallpine" src="https://avatars.githubusercontent.com/u/7328006?v=4&s=117" width="117">](https://github.com/profsmallpine) |[<img alt="jonathanarbely" src="https://avatars.githubusercontent.com/u/18177203?v=4&s=117" width="117">](https://github.com/jonathanarbely) |[<img alt="jderrough" src="https://avatars.githubusercontent.com/u/1108358?v=4&s=117" width="117">](https://github.com/jderrough) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[theJoeBiz](https://github.com/theJoeBiz) |[profsmallpine](https://github.com/profsmallpine) |[jonathanarbely](https://github.com/jonathanarbely) |[jderrough](https://github.com/jderrough) |[jonathanly](https://github.com/jonathanly) |[jorgeepc](https://github.com/jorgeepc) |
+[jhen0409](https://github.com/jhen0409) |[Jmales](https://github.com/Jmales) |[theJoeBiz](https://github.com/theJoeBiz) |[profsmallpine](https://github.com/profsmallpine) |[jonathanarbely](https://github.com/jonathanarbely) |[jderrough](https://github.com/jderrough) |
 
 
-[<img alt="jszobody" src="https://avatars.githubusercontent.com/u/203749?v=4&s=117" width="117">](https://github.com/jszobody) |[<img alt="julianocomg" src="https://avatars.githubusercontent.com/u/7483557?v=4&s=117" width="117">](https://github.com/julianocomg) |[<img alt="jmontoyaa" src="https://avatars.githubusercontent.com/u/158935?v=4&s=117" width="117">](https://github.com/jmontoyaa) |[<img alt="firesharkstudios" src="https://avatars.githubusercontent.com/u/17069637?v=4&s=117" width="117">](https://github.com/firesharkstudios) |[<img alt="elkebab" src="https://avatars.githubusercontent.com/u/6313468?v=4&s=117" width="117">](https://github.com/elkebab) |[<img alt="kyleparisi" src="https://avatars.githubusercontent.com/u/1286753?v=4&s=117" width="117">](https://github.com/kyleparisi) |
+[<img alt="jonathanly" src="https://avatars.githubusercontent.com/u/13286473?v=4&s=117" width="117">](https://github.com/jonathanly) |[<img alt="jorgeepc" src="https://avatars.githubusercontent.com/u/3879892?v=4&s=117" width="117">](https://github.com/jorgeepc) |[<img alt="jszobody" src="https://avatars.githubusercontent.com/u/203749?v=4&s=117" width="117">](https://github.com/jszobody) |[<img alt="julianocomg" src="https://avatars.githubusercontent.com/u/7483557?v=4&s=117" width="117">](https://github.com/julianocomg) |[<img alt="jmontoyaa" src="https://avatars.githubusercontent.com/u/158935?v=4&s=117" width="117">](https://github.com/jmontoyaa) |[<img alt="firesharkstudios" src="https://avatars.githubusercontent.com/u/17069637?v=4&s=117" width="117">](https://github.com/firesharkstudios) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[jszobody](https://github.com/jszobody) |[julianocomg](https://github.com/julianocomg) |[jmontoyaa](https://github.com/jmontoyaa) |[firesharkstudios](https://github.com/firesharkstudios) |[elkebab](https://github.com/elkebab) |[kyleparisi](https://github.com/kyleparisi) |
+[jonathanly](https://github.com/jonathanly) |[jorgeepc](https://github.com/jorgeepc) |[jszobody](https://github.com/jszobody) |[julianocomg](https://github.com/julianocomg) |[jmontoyaa](https://github.com/jmontoyaa) |[firesharkstudios](https://github.com/firesharkstudios) |
 
 
-[<img alt="lafe" src="https://avatars.githubusercontent.com/u/4070008?v=4&s=117" width="117">](https://github.com/lafe) |[<img alt="leaanthony" src="https://avatars.githubusercontent.com/u/1943904?v=4&s=117" width="117">](https://github.com/leaanthony) |[<img alt="larowlan" src="https://avatars.githubusercontent.com/u/555254?v=4&s=117" width="117">](https://github.com/larowlan) |[<img alt="dviry" src="https://avatars.githubusercontent.com/u/1230260?v=4&s=117" width="117">](https://github.com/dviry) |[<img alt="galli-leo" src="https://avatars.githubusercontent.com/u/5339762?v=4&s=117" width="117">](https://github.com/galli-leo) |[<img alt="leods92" src="https://avatars.githubusercontent.com/u/879395?v=4&s=117" width="117">](https://github.com/leods92) |
+[<img alt="elkebab" src="https://avatars.githubusercontent.com/u/6313468?v=4&s=117" width="117">](https://github.com/elkebab) |[<img alt="kyleparisi" src="https://avatars.githubusercontent.com/u/1286753?v=4&s=117" width="117">](https://github.com/kyleparisi) |[<img alt="lafe" src="https://avatars.githubusercontent.com/u/4070008?v=4&s=117" width="117">](https://github.com/lafe) |[<img alt="leaanthony" src="https://avatars.githubusercontent.com/u/1943904?v=4&s=117" width="117">](https://github.com/leaanthony) |[<img alt="larowlan" src="https://avatars.githubusercontent.com/u/555254?v=4&s=117" width="117">](https://github.com/larowlan) |[<img alt="dviry" src="https://avatars.githubusercontent.com/u/1230260?v=4&s=117" width="117">](https://github.com/dviry) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[lafe](https://github.com/lafe) |[leaanthony](https://github.com/leaanthony) |[larowlan](https://github.com/larowlan) |[dviry](https://github.com/dviry) |[galli-leo](https://github.com/galli-leo) |[leods92](https://github.com/leods92) |
+[elkebab](https://github.com/elkebab) |[kyleparisi](https://github.com/kyleparisi) |[lafe](https://github.com/lafe) |[leaanthony](https://github.com/leaanthony) |[larowlan](https://github.com/larowlan) |[dviry](https://github.com/dviry) |
 
 
-[<img alt="louim" src="https://avatars.githubusercontent.com/u/923718?v=4&s=117" width="117">](https://github.com/louim) |[<img alt="lucaperret" src="https://avatars.githubusercontent.com/u/1887122?v=4&s=117" width="117">](https://github.com/lucaperret) |[<img alt="mperrando" src="https://avatars.githubusercontent.com/u/525572?v=4&s=117" width="117">](https://github.com/mperrando) |[<img alt="marcosthejew" src="https://avatars.githubusercontent.com/u/1500967?v=4&s=117" width="117">](https://github.com/marcosthejew) |[<img alt="marcusforsberg" src="https://avatars.githubusercontent.com/u/1009069?v=4&s=117" width="117">](https://github.com/marcusforsberg) |[<img alt="Acconut" src="https://avatars.githubusercontent.com/u/1375043?v=4&s=117" width="117">](https://github.com/Acconut) |
+[<img alt="galli-leo" src="https://avatars.githubusercontent.com/u/5339762?v=4&s=117" width="117">](https://github.com/galli-leo) |[<img alt="leods92" src="https://avatars.githubusercontent.com/u/879395?v=4&s=117" width="117">](https://github.com/leods92) |[<img alt="louim" src="https://avatars.githubusercontent.com/u/923718?v=4&s=117" width="117">](https://github.com/louim) |[<img alt="lucaperret" src="https://avatars.githubusercontent.com/u/1887122?v=4&s=117" width="117">](https://github.com/lucaperret) |[<img alt="mperrando" src="https://avatars.githubusercontent.com/u/525572?v=4&s=117" width="117">](https://github.com/mperrando) |[<img alt="marcosthejew" src="https://avatars.githubusercontent.com/u/1500967?v=4&s=117" width="117">](https://github.com/marcosthejew) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[louim](https://github.com/louim) |[lucaperret](https://github.com/lucaperret) |[mperrando](https://github.com/mperrando) |[marcosthejew](https://github.com/marcosthejew) |[marcusforsberg](https://github.com/marcusforsberg) |[Acconut](https://github.com/Acconut) |
+[galli-leo](https://github.com/galli-leo) |[leods92](https://github.com/leods92) |[louim](https://github.com/louim) |[lucaperret](https://github.com/lucaperret) |[mperrando](https://github.com/mperrando) |[marcosthejew](https://github.com/marcosthejew) |
 
 
-[<img alt="masaok" src="https://avatars.githubusercontent.com/u/1320083?v=4&s=117" width="117">](https://github.com/masaok) |[<img alt="mattfik" src="https://avatars.githubusercontent.com/u/1638028?v=4&s=117" width="117">](https://github.com/mattfik) |[<img alt="matthewhartstonge" src="https://avatars.githubusercontent.com/u/6119549?v=4&s=117" width="117">](https://github.com/matthewhartstonge) |[<img alt="hrsh" src="https://avatars.githubusercontent.com/u/1929359?v=4&s=117" width="117">](https://github.com/hrsh) |[<img alt="mhulet" src="https://avatars.githubusercontent.com/u/293355?v=4&s=117" width="117">](https://github.com/mhulet) |[<img alt="mkopinsky" src="https://avatars.githubusercontent.com/u/591435?v=4&s=117" width="117">](https://github.com/mkopinsky) |
+[<img alt="marcusforsberg" src="https://avatars.githubusercontent.com/u/1009069?v=4&s=117" width="117">](https://github.com/marcusforsberg) |[<img alt="Acconut" src="https://avatars.githubusercontent.com/u/1375043?v=4&s=117" width="117">](https://github.com/Acconut) |[<img alt="masaok" src="https://avatars.githubusercontent.com/u/1320083?v=4&s=117" width="117">](https://github.com/masaok) |[<img alt="mattfik" src="https://avatars.githubusercontent.com/u/1638028?v=4&s=117" width="117">](https://github.com/mattfik) |[<img alt="matthewhartstonge" src="https://avatars.githubusercontent.com/u/6119549?v=4&s=117" width="117">](https://github.com/matthewhartstonge) |[<img alt="hrsh" src="https://avatars.githubusercontent.com/u/1929359?v=4&s=117" width="117">](https://github.com/hrsh) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[masaok](https://github.com/masaok) |[mattfik](https://github.com/mattfik) |[matthewhartstonge](https://github.com/matthewhartstonge) |[hrsh](https://github.com/hrsh) |[mhulet](https://github.com/mhulet) |[mkopinsky](https://github.com/mkopinsky) |
+[marcusforsberg](https://github.com/marcusforsberg) |[Acconut](https://github.com/Acconut) |[masaok](https://github.com/masaok) |[mattfik](https://github.com/mattfik) |[matthewhartstonge](https://github.com/matthewhartstonge) |[hrsh](https://github.com/hrsh) |
 
 
-[<img alt="achmiral" src="https://avatars.githubusercontent.com/u/10906059?v=4&s=117" width="117">](https://github.com/achmiral) |[<img alt="mnafees" src="https://avatars.githubusercontent.com/u/1763885?v=4&s=117" width="117">](https://github.com/mnafees) |[<img alt="shahimclt" src="https://avatars.githubusercontent.com/u/8318002?v=4&s=117" width="117">](https://github.com/shahimclt) |[<img alt="pleasespammelater" src="https://avatars.githubusercontent.com/u/11870394?v=4&s=117" width="117">](https://github.com/pleasespammelater) |[<img alt="naveed-ahmad" src="https://avatars.githubusercontent.com/u/701567?v=4&s=117" width="117">](https://github.com/naveed-ahmad) |[<img alt="nicojones" src="https://avatars.githubusercontent.com/u/6078915?v=4&s=117" width="117">](https://github.com/nicojones) |
+[<img alt="mhulet" src="https://avatars.githubusercontent.com/u/293355?v=4&s=117" width="117">](https://github.com/mhulet) |[<img alt="mkopinsky" src="https://avatars.githubusercontent.com/u/591435?v=4&s=117" width="117">](https://github.com/mkopinsky) |[<img alt="achmiral" src="https://avatars.githubusercontent.com/u/10906059?v=4&s=117" width="117">](https://github.com/achmiral) |[<img alt="mnafees" src="https://avatars.githubusercontent.com/u/1763885?v=4&s=117" width="117">](https://github.com/mnafees) |[<img alt="shahimclt" src="https://avatars.githubusercontent.com/u/8318002?v=4&s=117" width="117">](https://github.com/shahimclt) |[<img alt="pleasespammelater" src="https://avatars.githubusercontent.com/u/11870394?v=4&s=117" width="117">](https://github.com/pleasespammelater) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[achmiral](https://github.com/achmiral) |[mnafees](https://github.com/mnafees) |[shahimclt](https://github.com/shahimclt) |[pleasespammelater](https://github.com/pleasespammelater) |[naveed-ahmad](https://github.com/naveed-ahmad) |[nicojones](https://github.com/nicojones) |
+[mhulet](https://github.com/mhulet) |[mkopinsky](https://github.com/mkopinsky) |[achmiral](https://github.com/achmiral) |[mnafees](https://github.com/mnafees) |[shahimclt](https://github.com/shahimclt) |[pleasespammelater](https://github.com/pleasespammelater) |
 
 
-[<img alt="coreprocess" src="https://avatars.githubusercontent.com/u/1226918?v=4&s=117" width="117">](https://github.com/coreprocess) |[<img alt="nil1511" src="https://avatars.githubusercontent.com/u/2058170?v=4&s=117" width="117">](https://github.com/nil1511) |[<img alt="leftdevel" src="https://avatars.githubusercontent.com/u/843337?v=4&s=117" width="117">](https://github.com/leftdevel) |[<img alt="cryptic022" src="https://avatars.githubusercontent.com/u/18145703?v=4&s=117" width="117">](https://github.com/cryptic022) |[<img alt="patricklindsay" src="https://avatars.githubusercontent.com/u/7923681?v=4&s=117" width="117">](https://github.com/patricklindsay) |[<img alt="pedrofs" src="https://avatars.githubusercontent.com/u/56484?v=4&s=117" width="117">](https://github.com/pedrofs) |
+[<img alt="naveed-ahmad" src="https://avatars.githubusercontent.com/u/701567?v=4&s=117" width="117">](https://github.com/naveed-ahmad) |[<img alt="nicojones" src="https://avatars.githubusercontent.com/u/6078915?v=4&s=117" width="117">](https://github.com/nicojones) |[<img alt="coreprocess" src="https://avatars.githubusercontent.com/u/1226918?v=4&s=117" width="117">](https://github.com/coreprocess) |[<img alt="nil1511" src="https://avatars.githubusercontent.com/u/2058170?v=4&s=117" width="117">](https://github.com/nil1511) |[<img alt="leftdevel" src="https://avatars.githubusercontent.com/u/843337?v=4&s=117" width="117">](https://github.com/leftdevel) |[<img alt="cryptic022" src="https://avatars.githubusercontent.com/u/18145703?v=4&s=117" width="117">](https://github.com/cryptic022) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[coreprocess](https://github.com/coreprocess) |[nil1511](https://github.com/nil1511) |[leftdevel](https://github.com/leftdevel) |[cryptic022](https://github.com/cryptic022) |[patricklindsay](https://github.com/patricklindsay) |[pedrofs](https://github.com/pedrofs) |
+[naveed-ahmad](https://github.com/naveed-ahmad) |[nicojones](https://github.com/nicojones) |[coreprocess](https://github.com/coreprocess) |[nil1511](https://github.com/nil1511) |[leftdevel](https://github.com/leftdevel) |[cryptic022](https://github.com/cryptic022) |
 
 
-[<img alt="phillipalexander" src="https://avatars.githubusercontent.com/u/1577682?v=4&s=117" width="117">](https://github.com/phillipalexander) |[<img alt="ppadmavilasom" src="https://avatars.githubusercontent.com/u/11167452?v=4&s=117" width="117">](https://github.com/ppadmavilasom) |[<img alt="Pzoco" src="https://avatars.githubusercontent.com/u/3101348?v=4&s=117" width="117">](https://github.com/Pzoco) |[<img alt="eman8519" src="https://avatars.githubusercontent.com/u/2380804?v=4&s=117" width="117">](https://github.com/eman8519) |[<img alt="luarmr" src="https://avatars.githubusercontent.com/u/817416?v=4&s=117" width="117">](https://github.com/luarmr) |[<img alt="SxDx" src="https://avatars.githubusercontent.com/u/2004247?v=4&s=117" width="117">](https://github.com/SxDx) |
+[<img alt="paescuj" src="https://avatars.githubusercontent.com/u/5363448?v=4&s=117" width="117">](https://github.com/paescuj) |[<img alt="patricklindsay" src="https://avatars.githubusercontent.com/u/7923681?v=4&s=117" width="117">](https://github.com/patricklindsay) |[<img alt="pedrofs" src="https://avatars.githubusercontent.com/u/56484?v=4&s=117" width="117">](https://github.com/pedrofs) |[<img alt="phillipalexander" src="https://avatars.githubusercontent.com/u/1577682?v=4&s=117" width="117">](https://github.com/phillipalexander) |[<img alt="ppadmavilasom" src="https://avatars.githubusercontent.com/u/11167452?v=4&s=117" width="117">](https://github.com/ppadmavilasom) |[<img alt="Pzoco" src="https://avatars.githubusercontent.com/u/3101348?v=4&s=117" width="117">](https://github.com/Pzoco) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[phillipalexander](https://github.com/phillipalexander) |[ppadmavilasom](https://github.com/ppadmavilasom) |[Pzoco](https://github.com/Pzoco) |[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[SxDx](https://github.com/SxDx) |
+[paescuj](https://github.com/paescuj) |[patricklindsay](https://github.com/patricklindsay) |[pedrofs](https://github.com/pedrofs) |[phillipalexander](https://github.com/phillipalexander) |[ppadmavilasom](https://github.com/ppadmavilasom) |[Pzoco](https://github.com/Pzoco) |
 
 
-[<img alt="phobos101" src="https://avatars.githubusercontent.com/u/7114944?v=4&s=117" width="117">](https://github.com/phobos101) |[<img alt="romain-preston" src="https://avatars.githubusercontent.com/u/1517040?v=4&s=117" width="117">](https://github.com/romain-preston) |[<img alt="scherroman" src="https://avatars.githubusercontent.com/u/7923938?v=4&s=117" width="117">](https://github.com/scherroman) |[<img alt="rart" src="https://avatars.githubusercontent.com/u/3928341?v=4&s=117" width="117">](https://github.com/rart) |[<img alt="fortunto2" src="https://avatars.githubusercontent.com/u/1236751?v=4&s=117" width="117">](https://github.com/fortunto2) |[<img alt="samuelcolburn" src="https://avatars.githubusercontent.com/u/9741902?v=4&s=117" width="117">](https://github.com/samuelcolburn) |
+[<img alt="eman8519" src="https://avatars.githubusercontent.com/u/2380804?v=4&s=117" width="117">](https://github.com/eman8519) |[<img alt="luarmr" src="https://avatars.githubusercontent.com/u/817416?v=4&s=117" width="117">](https://github.com/luarmr) |[<img alt="SxDx" src="https://avatars.githubusercontent.com/u/2004247?v=4&s=117" width="117">](https://github.com/SxDx) |[<img alt="phobos101" src="https://avatars.githubusercontent.com/u/7114944?v=4&s=117" width="117">](https://github.com/phobos101) |[<img alt="romain-preston" src="https://avatars.githubusercontent.com/u/1517040?v=4&s=117" width="117">](https://github.com/romain-preston) |[<img alt="scherroman" src="https://avatars.githubusercontent.com/u/7923938?v=4&s=117" width="117">](https://github.com/scherroman) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[phobos101](https://github.com/phobos101) |[romain-preston](https://github.com/romain-preston) |[scherroman](https://github.com/scherroman) |[rart](https://github.com/rart) |[fortunto2](https://github.com/fortunto2) |[samuelcolburn](https://github.com/samuelcolburn) |
+[eman8519](https://github.com/eman8519) |[luarmr](https://github.com/luarmr) |[SxDx](https://github.com/SxDx) |[phobos101](https://github.com/phobos101) |[romain-preston](https://github.com/romain-preston) |[scherroman](https://github.com/scherroman) |
 
 
-[<img alt="sergei-zelinsky" src="https://avatars.githubusercontent.com/u/19428086?v=4&s=117" width="117">](https://github.com/sergei-zelinsky) |[<img alt="SpazzMarticus" src="https://avatars.githubusercontent.com/u/5716457?v=4&s=117" width="117">](https://github.com/SpazzMarticus) |[<img alt="waptik" src="https://avatars.githubusercontent.com/u/1687551?v=4&s=117" width="117">](https://github.com/waptik) |[<img alt="steverob" src="https://avatars.githubusercontent.com/u/1220480?v=4&s=117" width="117">](https://github.com/steverob) |[<img alt="taj" src="https://avatars.githubusercontent.com/u/16062635?v=4&s=117" width="117">](https://github.com/taj) |[<img alt="Tashows" src="https://avatars.githubusercontent.com/u/16656928?v=4&s=117" width="117">](https://github.com/Tashows) |
+[<img alt="rart" src="https://avatars.githubusercontent.com/u/3928341?v=4&s=117" width="117">](https://github.com/rart) |[<img alt="fortunto2" src="https://avatars.githubusercontent.com/u/1236751?v=4&s=117" width="117">](https://github.com/fortunto2) |[<img alt="samuelcolburn" src="https://avatars.githubusercontent.com/u/9741902?v=4&s=117" width="117">](https://github.com/samuelcolburn) |[<img alt="sergei-zelinsky" src="https://avatars.githubusercontent.com/u/19428086?v=4&s=117" width="117">](https://github.com/sergei-zelinsky) |[<img alt="SpazzMarticus" src="https://avatars.githubusercontent.com/u/5716457?v=4&s=117" width="117">](https://github.com/SpazzMarticus) |[<img alt="waptik" src="https://avatars.githubusercontent.com/u/1687551?v=4&s=117" width="117">](https://github.com/waptik) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[sergei-zelinsky](https://github.com/sergei-zelinsky) |[SpazzMarticus](https://github.com/SpazzMarticus) |[waptik](https://github.com/waptik) |[steverob](https://github.com/steverob) |[taj](https://github.com/taj) |[Tashows](https://github.com/Tashows) |
+[rart](https://github.com/rart) |[fortunto2](https://github.com/fortunto2) |[samuelcolburn](https://github.com/samuelcolburn) |[sergei-zelinsky](https://github.com/sergei-zelinsky) |[SpazzMarticus](https://github.com/SpazzMarticus) |[waptik](https://github.com/waptik) |
 
 
-[<img alt="twarlop" src="https://avatars.githubusercontent.com/u/2856082?v=4&s=117" width="117">](https://github.com/twarlop) |[<img alt="tmaier" src="https://avatars.githubusercontent.com/u/350038?v=4&s=117" width="117">](https://github.com/tmaier) |[<img alt="tomsaleeba" src="https://avatars.githubusercontent.com/u/1773838?v=4&s=117" width="117">](https://github.com/tomsaleeba) |[<img alt="tvaliasek" src="https://avatars.githubusercontent.com/u/8644946?v=4&s=117" width="117">](https://github.com/tvaliasek) |[<img alt="vially" src="https://avatars.githubusercontent.com/u/433598?v=4&s=117" width="117">](https://github.com/vially) |[<img alt="nagyv" src="https://avatars.githubusercontent.com/u/126671?v=4&s=117" width="117">](https://github.com/nagyv) |
+[<img alt="steverob" src="https://avatars.githubusercontent.com/u/1220480?v=4&s=117" width="117">](https://github.com/steverob) |[<img alt="taj" src="https://avatars.githubusercontent.com/u/16062635?v=4&s=117" width="117">](https://github.com/taj) |[<img alt="Tashows" src="https://avatars.githubusercontent.com/u/16656928?v=4&s=117" width="117">](https://github.com/Tashows) |[<img alt="twarlop" src="https://avatars.githubusercontent.com/u/2856082?v=4&s=117" width="117">](https://github.com/twarlop) |[<img alt="tmaier" src="https://avatars.githubusercontent.com/u/350038?v=4&s=117" width="117">](https://github.com/tmaier) |[<img alt="tomsaleeba" src="https://avatars.githubusercontent.com/u/1773838?v=4&s=117" width="117">](https://github.com/tomsaleeba) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[twarlop](https://github.com/twarlop) |[tmaier](https://github.com/tmaier) |[tomsaleeba](https://github.com/tomsaleeba) |[tvaliasek](https://github.com/tvaliasek) |[vially](https://github.com/vially) |[nagyv](https://github.com/nagyv) |
+[steverob](https://github.com/steverob) |[taj](https://github.com/taj) |[Tashows](https://github.com/Tashows) |[twarlop](https://github.com/twarlop) |[tmaier](https://github.com/tmaier) |[tomsaleeba](https://github.com/tomsaleeba) |
 
 
-[<img alt="willycamargo" src="https://avatars.githubusercontent.com/u/5041887?v=4&s=117" width="117">](https://github.com/willycamargo) |[<img alt="xhocquet" src="https://avatars.githubusercontent.com/u/8116516?v=4&s=117" width="117">](https://github.com/xhocquet) |[<img alt="yaegor" src="https://avatars.githubusercontent.com/u/3315?v=4&s=117" width="117">](https://github.com/yaegor) |[<img alt="YehudaKremer" src="https://avatars.githubusercontent.com/u/946652?v=4&s=117" width="117">](https://github.com/YehudaKremer) |[<img alt="zachconner" src="https://avatars.githubusercontent.com/u/11339326?v=4&s=117" width="117">](https://github.com/zachconner) |[<img alt="zacharylawson" src="https://avatars.githubusercontent.com/u/7375444?v=4&s=117" width="117">](https://github.com/zacharylawson) |
+[<img alt="tvaliasek" src="https://avatars.githubusercontent.com/u/8644946?v=4&s=117" width="117">](https://github.com/tvaliasek) |[<img alt="vially" src="https://avatars.githubusercontent.com/u/433598?v=4&s=117" width="117">](https://github.com/vially) |[<img alt="nagyv" src="https://avatars.githubusercontent.com/u/126671?v=4&s=117" width="117">](https://github.com/nagyv) |[<img alt="dwnste" src="https://avatars.githubusercontent.com/u/17119722?v=4&s=117" width="117">](https://github.com/dwnste) |[<img alt="willycamargo" src="https://avatars.githubusercontent.com/u/5041887?v=4&s=117" width="117">](https://github.com/willycamargo) |[<img alt="xhocquet" src="https://avatars.githubusercontent.com/u/8116516?v=4&s=117" width="117">](https://github.com/xhocquet) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[willycamargo](https://github.com/willycamargo) |[xhocquet](https://github.com/xhocquet) |[yaegor](https://github.com/yaegor) |[YehudaKremer](https://github.com/YehudaKremer) |[zachconner](https://github.com/zachconner) |[zacharylawson](https://github.com/zacharylawson) |
+[tvaliasek](https://github.com/tvaliasek) |[vially](https://github.com/vially) |[nagyv](https://github.com/nagyv) |[dwnste](https://github.com/dwnste) |[willycamargo](https://github.com/willycamargo) |[xhocquet](https://github.com/xhocquet) |
 
 
-[<img alt="agreene-coursera" src="https://avatars.githubusercontent.com/u/30501355?v=4&s=117" width="117">](https://github.com/agreene-coursera) |[<img alt="alfatv" src="https://avatars.githubusercontent.com/u/62238673?v=4&s=117" width="117">](https://github.com/alfatv) |[<img alt="anark" src="https://avatars.githubusercontent.com/u/101184?v=4&s=117" width="117">](https://github.com/anark) |[<img alt="arggh" src="https://avatars.githubusercontent.com/u/17210302?v=4&s=117" width="117">](https://github.com/arggh) |[<img alt="avalla" src="https://avatars.githubusercontent.com/u/986614?v=4&s=117" width="117">](https://github.com/avalla) |[<img alt="bdirito" src="https://avatars.githubusercontent.com/u/8117238?v=4&s=117" width="117">](https://github.com/bdirito) |
+[<img alt="yaegor" src="https://avatars.githubusercontent.com/u/3315?v=4&s=117" width="117">](https://github.com/yaegor) |[<img alt="YehudaKremer" src="https://avatars.githubusercontent.com/u/946652?v=4&s=117" width="117">](https://github.com/YehudaKremer) |[<img alt="zachconner" src="https://avatars.githubusercontent.com/u/11339326?v=4&s=117" width="117">](https://github.com/zachconner) |[<img alt="zacharylawson" src="https://avatars.githubusercontent.com/u/7375444?v=4&s=117" width="117">](https://github.com/zacharylawson) |[<img alt="agreene-coursera" src="https://avatars.githubusercontent.com/u/30501355?v=4&s=117" width="117">](https://github.com/agreene-coursera) |[<img alt="alfatv" src="https://avatars.githubusercontent.com/u/62238673?v=4&s=117" width="117">](https://github.com/alfatv) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[agreene-coursera](https://github.com/agreene-coursera) |[alfatv](https://github.com/alfatv) |[anark](https://github.com/anark) |[arggh](https://github.com/arggh) |[avalla](https://github.com/avalla) |[bdirito](https://github.com/bdirito) |
+[yaegor](https://github.com/yaegor) |[YehudaKremer](https://github.com/YehudaKremer) |[zachconner](https://github.com/zachconner) |[zacharylawson](https://github.com/zacharylawson) |[agreene-coursera](https://github.com/agreene-coursera) |[alfatv](https://github.com/alfatv) |
 
 
-[<img alt="c0b41" src="https://avatars.githubusercontent.com/u/2834954?v=4&s=117" width="117">](https://github.com/c0b41) |[<img alt="canvasbh" src="https://avatars.githubusercontent.com/u/44477734?v=4&s=117" width="117">](https://github.com/canvasbh) |[<img alt="craigcbrunner" src="https://avatars.githubusercontent.com/u/2780521?v=4&s=117" width="117">](https://github.com/craigcbrunner) |[<img alt="darthf1" src="https://avatars.githubusercontent.com/u/17253332?v=4&s=117" width="117">](https://github.com/darthf1) |[<img alt="dkisic" src="https://avatars.githubusercontent.com/u/32257921?v=4&s=117" width="117">](https://github.com/dkisic) |[<img alt="fingul" src="https://avatars.githubusercontent.com/u/894739?v=4&s=117" width="117">](https://github.com/fingul) |
+[<img alt="anark" src="https://avatars.githubusercontent.com/u/101184?v=4&s=117" width="117">](https://github.com/anark) |[<img alt="arggh" src="https://avatars.githubusercontent.com/u/17210302?v=4&s=117" width="117">](https://github.com/arggh) |[<img alt="avalla" src="https://avatars.githubusercontent.com/u/986614?v=4&s=117" width="117">](https://github.com/avalla) |[<img alt="bdirito" src="https://avatars.githubusercontent.com/u/8117238?v=4&s=117" width="117">](https://github.com/bdirito) |[<img alt="c0b41" src="https://avatars.githubusercontent.com/u/2834954?v=4&s=117" width="117">](https://github.com/c0b41) |[<img alt="canvasbh" src="https://avatars.githubusercontent.com/u/44477734?v=4&s=117" width="117">](https://github.com/canvasbh) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[c0b41](https://github.com/c0b41) |[canvasbh](https://github.com/canvasbh) |[craigcbrunner](https://github.com/craigcbrunner) |[darthf1](https://github.com/darthf1) |[dkisic](https://github.com/dkisic) |[fingul](https://github.com/fingul) |
+[anark](https://github.com/anark) |[arggh](https://github.com/arggh) |[avalla](https://github.com/avalla) |[bdirito](https://github.com/bdirito) |[c0b41](https://github.com/c0b41) |[canvasbh](https://github.com/canvasbh) |
 
 
-[<img alt="franckl" src="https://avatars.githubusercontent.com/u/3875803?v=4&s=117" width="117">](https://github.com/franckl) |[<img alt="gaelicwinter" src="https://avatars.githubusercontent.com/u/6510266?v=4&s=117" width="117">](https://github.com/gaelicwinter) |[<img alt="green-mike" src="https://avatars.githubusercontent.com/u/5584225?v=4&s=117" width="117">](https://github.com/green-mike) |[<img alt="hxgf" src="https://avatars.githubusercontent.com/u/56104?v=4&s=117" width="117">](https://github.com/hxgf) |[<img alt="johnmanjiro13" src="https://avatars.githubusercontent.com/u/28798279?v=4&s=117" width="117">](https://github.com/johnmanjiro13) |[<img alt="kode-ninja" src="https://avatars.githubusercontent.com/u/7857611?v=4&s=117" width="117">](https://github.com/kode-ninja) |
+[<img alt="craigcbrunner" src="https://avatars.githubusercontent.com/u/2780521?v=4&s=117" width="117">](https://github.com/craigcbrunner) |[<img alt="darthf1" src="https://avatars.githubusercontent.com/u/17253332?v=4&s=117" width="117">](https://github.com/darthf1) |[<img alt="dkisic" src="https://avatars.githubusercontent.com/u/32257921?v=4&s=117" width="117">](https://github.com/dkisic) |[<img alt="fingul" src="https://avatars.githubusercontent.com/u/894739?v=4&s=117" width="117">](https://github.com/fingul) |[<img alt="franckl" src="https://avatars.githubusercontent.com/u/3875803?v=4&s=117" width="117">](https://github.com/franckl) |[<img alt="gaelicwinter" src="https://avatars.githubusercontent.com/u/6510266?v=4&s=117" width="117">](https://github.com/gaelicwinter) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[franckl](https://github.com/franckl) |[gaelicwinter](https://github.com/gaelicwinter) |[green-mike](https://github.com/green-mike) |[hxgf](https://github.com/hxgf) |[johnmanjiro13](https://github.com/johnmanjiro13) |[kode-ninja](https://github.com/kode-ninja) |
+[craigcbrunner](https://github.com/craigcbrunner) |[darthf1](https://github.com/darthf1) |[dkisic](https://github.com/dkisic) |[fingul](https://github.com/fingul) |[franckl](https://github.com/franckl) |[gaelicwinter](https://github.com/gaelicwinter) |
 
 
-[<img alt="magumbo" src="https://avatars.githubusercontent.com/u/6683765?v=4&s=117" width="117">](https://github.com/magumbo) |[<img alt="ninesalt" src="https://avatars.githubusercontent.com/u/7952255?v=4&s=117" width="117">](https://github.com/ninesalt) |[<img alt="phil714" src="https://avatars.githubusercontent.com/u/7584581?v=4&s=117" width="117">](https://github.com/phil714) |[<img alt="luntta" src="https://avatars.githubusercontent.com/u/14221637?v=4&s=117" width="117">](https://github.com/luntta) |[<img alt="rhymes" src="https://avatars.githubusercontent.com/u/146201?v=4&s=117" width="117">](https://github.com/rhymes) |[<img alt="rlebosse" src="https://avatars.githubusercontent.com/u/2794137?v=4&s=117" width="117">](https://github.com/rlebosse) |
+[<img alt="green-mike" src="https://avatars.githubusercontent.com/u/5584225?v=4&s=117" width="117">](https://github.com/green-mike) |[<img alt="hxgf" src="https://avatars.githubusercontent.com/u/56104?v=4&s=117" width="117">](https://github.com/hxgf) |[<img alt="johnmanjiro13" src="https://avatars.githubusercontent.com/u/28798279?v=4&s=117" width="117">](https://github.com/johnmanjiro13) |[<img alt="kode-ninja" src="https://avatars.githubusercontent.com/u/7857611?v=4&s=117" width="117">](https://github.com/kode-ninja) |[<img alt="magumbo" src="https://avatars.githubusercontent.com/u/6683765?v=4&s=117" width="117">](https://github.com/magumbo) |[<img alt="ninesalt" src="https://avatars.githubusercontent.com/u/7952255?v=4&s=117" width="117">](https://github.com/ninesalt) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[magumbo](https://github.com/magumbo) |[ninesalt](https://github.com/ninesalt) |[phil714](https://github.com/phil714) |[luntta](https://github.com/luntta) |[rhymes](https://github.com/rhymes) |[rlebosse](https://github.com/rlebosse) |
+[green-mike](https://github.com/green-mike) |[hxgf](https://github.com/hxgf) |[johnmanjiro13](https://github.com/johnmanjiro13) |[kode-ninja](https://github.com/kode-ninja) |[magumbo](https://github.com/magumbo) |[ninesalt](https://github.com/ninesalt) |
 
 
-[<img alt="rtaieb" src="https://avatars.githubusercontent.com/u/35224301?v=4&s=117" width="117">](https://github.com/rtaieb) |[<img alt="slawexxx44" src="https://avatars.githubusercontent.com/u/11180644?v=4&s=117" width="117">](https://github.com/slawexxx44) |[<img alt="thanhthot" src="https://avatars.githubusercontent.com/u/50633205?v=4&s=117" width="117">](https://github.com/thanhthot) |[<img alt="tinny77" src="https://avatars.githubusercontent.com/u/1872936?v=4&s=117" width="117">](https://github.com/tinny77) |[<img alt="vedran555" src="https://avatars.githubusercontent.com/u/38395951?v=4&s=117" width="117">](https://github.com/vedran555) |[<img alt="yoann-hellopret" src="https://avatars.githubusercontent.com/u/46525558?v=4&s=117" width="117">](https://github.com/yoann-hellopret) |
+[<img alt="phil714" src="https://avatars.githubusercontent.com/u/7584581?v=4&s=117" width="117">](https://github.com/phil714) |[<img alt="luntta" src="https://avatars.githubusercontent.com/u/14221637?v=4&s=117" width="117">](https://github.com/luntta) |[<img alt="rhymes" src="https://avatars.githubusercontent.com/u/146201?v=4&s=117" width="117">](https://github.com/rhymes) |[<img alt="rlebosse" src="https://avatars.githubusercontent.com/u/2794137?v=4&s=117" width="117">](https://github.com/rlebosse) |[<img alt="rtaieb" src="https://avatars.githubusercontent.com/u/35224301?v=4&s=117" width="117">](https://github.com/rtaieb) |[<img alt="slawexxx44" src="https://avatars.githubusercontent.com/u/11180644?v=4&s=117" width="117">](https://github.com/slawexxx44) |
 :---: |:---: |:---: |:---: |:---: |:---: |
 :---: |:---: |:---: |:---: |:---: |:---: |
-[rtaieb](https://github.com/rtaieb) |[slawexxx44](https://github.com/slawexxx44) |[thanhthot](https://github.com/thanhthot) |[tinny77](https://github.com/tinny77) |[vedran555](https://github.com/vedran555) |[yoann-hellopret](https://github.com/yoann-hellopret) |
+[phil714](https://github.com/phil714) |[luntta](https://github.com/luntta) |[rhymes](https://github.com/rhymes) |[rlebosse](https://github.com/rlebosse) |[rtaieb](https://github.com/rtaieb) |[slawexxx44](https://github.com/slawexxx44) |
 
 
-[<img alt="olitomas" src="https://avatars.githubusercontent.com/u/6918659?v=4&s=117" width="117">](https://github.com/olitomas) |[<img alt="JimmyLv" src="https://avatars.githubusercontent.com/u/4997466?v=4&s=117" width="117">](https://github.com/JimmyLv) |
-:---: |:---: |
-[olitomas](https://github.com/olitomas) |[JimmyLv](https://github.com/JimmyLv) |
+[<img alt="thanhthot" src="https://avatars.githubusercontent.com/u/50633205?v=4&s=117" width="117">](https://github.com/thanhthot) |[<img alt="tinny77" src="https://avatars.githubusercontent.com/u/1872936?v=4&s=117" width="117">](https://github.com/tinny77) |[<img alt="vedran555" src="https://avatars.githubusercontent.com/u/38395951?v=4&s=117" width="117">](https://github.com/vedran555) |[<img alt="yoann-hellopret" src="https://avatars.githubusercontent.com/u/46525558?v=4&s=117" width="117">](https://github.com/yoann-hellopret) |[<img alt="olitomas" src="https://avatars.githubusercontent.com/u/6918659?v=4&s=117" width="117">](https://github.com/olitomas) |[<img alt="JimmyLv" src="https://avatars.githubusercontent.com/u/4997466?v=4&s=117" width="117">](https://github.com/JimmyLv) |
+:---: |:---: |:---: |:---: |:---: |:---: |
+[thanhthot](https://github.com/thanhthot) |[tinny77](https://github.com/tinny77) |[vedran555](https://github.com/vedran555) |[yoann-hellopret](https://github.com/yoann-hellopret) |[olitomas](https://github.com/olitomas) |[JimmyLv](https://github.com/JimmyLv) |
 
 
 
 
 <!--/contributors-->
 <!--/contributors-->

+ 1 - 0
examples/bundled/index.js

@@ -32,6 +32,7 @@ const uppy = new Uppy({
     showProgressDetails: true,
     showProgressDetails: true,
     proudlyDisplayPoweredByUppy: true,
     proudlyDisplayPoweredByUppy: true,
     note: '2 files, images and video only',
     note: '2 files, images and video only',
+    restrictions: { requiredMetaFields: ['caption'] },
   })
   })
   .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })

+ 2 - 2
examples/cdn-example/index.html

@@ -4,11 +4,11 @@
     <title></title>
     <title></title>
     <meta charset="UTF-8">
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css" rel="stylesheet">
+    <link href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
+    <script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
     <script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 1 - 0
examples/dev/Dashboard.js

@@ -58,6 +58,7 @@ module.exports = () => {
       username: 'John',
       username: 'John',
       license: 'Creative Commons',
       license: 'Creative Commons',
     },
     },
+    restrictions: { requiredMetaFields: ['caption'] },
   })
   })
     .use(Dashboard, {
     .use(Dashboard, {
       trigger: '#pick-files',
       trigger: '#pick-files',

+ 1 - 1
examples/transloadit-textarea/index.html

@@ -2,7 +2,7 @@
 <html>
 <html>
   <head>
   <head>
     <meta charset="utf-8">
     <meta charset="utf-8">
-    <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.css">
+    <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.css">
     <style>
     <style>
       body {
       body {
         font-family: Roboto, Open Sans;
         font-family: Roboto, Open Sans;

+ 2 - 2
examples/uppy-with-companion/client/index.html

@@ -4,11 +4,11 @@
     <title></title>
     <title></title>
     <meta charset="UTF-8">
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css" rel="stylesheet">
+    <link href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>
     <button id="uppyModalOpener">Open Modal</button>
     <button id="uppyModalOpener">Open Modal</button>
-    <script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
+    <script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
     <script>
     <script>
       const uppy = Uppy.Core({debug: true, autoProceed: false})
       const uppy = Uppy.Core({debug: true, autoProceed: false})
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })
         .use(Uppy.Dashboard, { trigger: '#uppyModalOpener' })

+ 16 - 16
package-lock.json

@@ -75401,7 +75401,7 @@
       }
       }
     },
     },
     "packages/@uppy/angular": {
     "packages/@uppy/angular": {
-      "version": "0.2.0",
+      "version": "0.1.3",
       "dependencies": {
       "dependencies": {
         "@angular/animations": "~12.1.0",
         "@angular/animations": "~12.1.0",
         "@angular/common": "~12.1.0",
         "@angular/common": "~12.1.0",
@@ -75489,7 +75489,7 @@
       "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
       "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
     },
     },
     "packages/@uppy/aws-s3": {
     "packages/@uppy/aws-s3": {
-      "version": "1.7.12",
+      "version": "1.8.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/companion-client": "file:../companion-client",
         "@uppy/companion-client": "file:../companion-client",
@@ -75530,7 +75530,7 @@
       }
       }
     },
     },
     "packages/@uppy/companion": {
     "packages/@uppy/companion": {
-      "version": "2.11.0",
+      "version": "2.12.0",
       "license": "ISC",
       "license": "ISC",
       "dependencies": {
       "dependencies": {
         "@purest/providers": "1.0.1",
         "@purest/providers": "1.0.1",
@@ -75837,7 +75837,7 @@
       }
       }
     },
     },
     "packages/@uppy/core": {
     "packages/@uppy/core": {
-      "version": "1.19.2",
+      "version": "1.20.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@transloadit/prettier-bytes": "0.0.7",
         "@transloadit/prettier-bytes": "0.0.7",
@@ -75851,7 +75851,7 @@
       }
       }
     },
     },
     "packages/@uppy/dashboard": {
     "packages/@uppy/dashboard": {
-      "version": "1.20.2",
+      "version": "1.21.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@transloadit/prettier-bytes": "0.0.7",
         "@transloadit/prettier-bytes": "0.0.7",
@@ -75877,7 +75877,7 @@
       }
       }
     },
     },
     "packages/@uppy/drag-drop": {
     "packages/@uppy/drag-drop": {
-      "version": "1.4.30",
+      "version": "1.4.31",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/utils": "file:../utils",
         "@uppy/utils": "file:../utils",
@@ -75971,7 +75971,7 @@
       }
       }
     },
     },
     "packages/@uppy/image-editor": {
     "packages/@uppy/image-editor": {
-      "version": "0.3.0",
+      "version": "0.4.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/utils": "file:../utils",
         "@uppy/utils": "file:../utils",
@@ -76007,7 +76007,7 @@
       }
       }
     },
     },
     "packages/@uppy/locales": {
     "packages/@uppy/locales": {
-      "version": "1.21.0",
+      "version": "1.22.0",
       "license": "MIT"
       "license": "MIT"
     },
     },
     "packages/@uppy/onedrive": {
     "packages/@uppy/onedrive": {
@@ -76047,7 +76047,7 @@
       }
       }
     },
     },
     "packages/@uppy/react": {
     "packages/@uppy/react": {
-      "version": "1.12.0",
+      "version": "1.12.1",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/dashboard": "file:../dashboard",
         "@uppy/dashboard": "file:../dashboard",
@@ -76094,7 +76094,7 @@
       }
       }
     },
     },
     "packages/@uppy/robodog": {
     "packages/@uppy/robodog": {
-      "version": "1.10.12",
+      "version": "1.11.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/core": "file:../core",
         "@uppy/core": "file:../core",
@@ -76117,7 +76117,7 @@
       }
       }
     },
     },
     "packages/@uppy/screen-capture": {
     "packages/@uppy/screen-capture": {
-      "version": "1.0.21",
+      "version": "1.1.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/utils": "file:../utils",
         "@uppy/utils": "file:../utils",
@@ -76175,7 +76175,7 @@
       }
       }
     },
     },
     "packages/@uppy/svelte": {
     "packages/@uppy/svelte": {
-      "version": "0.1.12",
+      "version": "0.1.13",
       "dependencies": {
       "dependencies": {
         "@uppy/dashboard": "file:../dashboard",
         "@uppy/dashboard": "file:../dashboard",
         "@uppy/drag-drop": "file:../drag-drop",
         "@uppy/drag-drop": "file:../drag-drop",
@@ -76231,7 +76231,7 @@
       }
       }
     },
     },
     "packages/@uppy/transloadit": {
     "packages/@uppy/transloadit": {
-      "version": "1.6.26",
+      "version": "1.7.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/companion-client": "file:../companion-client",
         "@uppy/companion-client": "file:../companion-client",
@@ -76293,7 +76293,7 @@
       }
       }
     },
     },
     "packages/@uppy/vue": {
     "packages/@uppy/vue": {
-      "version": "0.2.5",
+      "version": "0.2.6",
       "dependencies": {
       "dependencies": {
         "@uppy/dashboard": "file:../dashboard",
         "@uppy/dashboard": "file:../dashboard",
         "@uppy/drag-drop": "file:../drag-drop",
         "@uppy/drag-drop": "file:../drag-drop",
@@ -76310,7 +76310,7 @@
       }
       }
     },
     },
     "packages/@uppy/webcam": {
     "packages/@uppy/webcam": {
-      "version": "1.8.12",
+      "version": "1.8.13",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/utils": "file:../utils",
         "@uppy/utils": "file:../utils",
@@ -76349,7 +76349,7 @@
       }
       }
     },
     },
     "packages/uppy": {
     "packages/uppy": {
-      "version": "1.30.0",
+      "version": "1.31.0",
       "license": "MIT",
       "license": "MIT",
       "dependencies": {
       "dependencies": {
         "@uppy/aws-s3": "file:../@uppy/aws-s3",
         "@uppy/aws-s3": "file:../@uppy/aws-s3",

+ 1 - 1
package.json

@@ -130,7 +130,7 @@
     "build:companion": "npm run --prefix ./packages/@uppy/companion build",
     "build:companion": "npm run --prefix ./packages/@uppy/companion build",
     "build:css": "node ./bin/build-css.js",
     "build:css": "node ./bin/build-css.js",
     "build:svelte": "npm run --prefix ./packages/@uppy/svelte build",
     "build:svelte": "npm run --prefix ./packages/@uppy/svelte build",
-    "build:angular": "npm run --prefix ./packages/@uppy/angular build",
+    "build:angular": "npm run --prefix ./packages/@uppy/angular build:release",
     "build:js": "npm-run-all build:lib build:companion build:locale-pack build:svelte build:angular build:bundle",
     "build:js": "npm-run-all build:lib build:companion build:locale-pack build:svelte build:angular build:bundle",
     "build:lib": "node ./bin/build-lib.js",
     "build:lib": "node ./bin/build-lib.js",
     "build:locale-pack": "node ./bin/locale-packs.js build",
     "build:locale-pack": "node ./bin/locale-packs.js build",

+ 6 - 3
packages/@uppy/angular/package.json

@@ -1,13 +1,13 @@
 {
 {
   "name": "@uppy/angular",
   "name": "@uppy/angular",
-  "version": "0.2.0",
+  "version": "0.1.3",
   "module": "dist/angular/esm2015/public-api.js",
   "module": "dist/angular/esm2015/public-api.js",
   "types": "dist/angular/uppy-angular.d.ts",
   "types": "dist/angular/uppy-angular.d.ts",
   "scripts": {
   "scripts": {
     "ng": "ng",
     "ng": "ng",
     "start": "start-storybook -p 6006",
     "start": "start-storybook -p 6006",
     "build": "ng build",
     "build": "ng build",
-    "release": "ng build --prod",
+    "build:release": "ng build --prod",
     "test": "ng test",
     "test": "ng test",
     "lint": "ng lint",
     "lint": "ng lint",
     "e2e": "ng e2e",
     "e2e": "ng e2e",
@@ -75,5 +75,8 @@
   },
   },
   "publishConfig": {
   "publishConfig": {
     "access": "public"
     "access": "public"
-  }
+  },
+  "files": [
+    "/dist"
+  ]
 }
 }

+ 1 - 1
packages/@uppy/aws-s3/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/aws-s3",
   "name": "@uppy/aws-s3",
   "description": "Upload to Amazon S3 with Uppy",
   "description": "Upload to Amazon S3 with Uppy",
-  "version": "1.7.12",
+  "version": "1.8.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
   "types": "types/index.d.ts",

+ 1 - 0
packages/@uppy/aws-s3/src/MiniXHRUpload.js

@@ -332,6 +332,7 @@ module.exports = class MiniXHRUpload {
             status: data.response.status,
             status: data.response.status,
             body,
             body,
             uploadURL,
             uploadURL,
+            bytesUploaded: data.bytesUploaded,
           }
           }
 
 
           this.uppy.emit('upload-success', file, uploadResp)
           this.uppy.emit('upload-success', file, uploadResp)

+ 1 - 1
packages/@uppy/companion/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@uppy/companion",
   "name": "@uppy/companion",
-  "version": "2.11.0",
+  "version": "2.12.0",
   "description": "OAuth helper and remote fetcher for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:",
   "description": "OAuth helper and remote fetcher for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:",
   "main": "lib/companion.js",
   "main": "lib/companion.js",
   "types": "lib/companion.d.ts",
   "types": "lib/companion.d.ts",

+ 1 - 1
packages/@uppy/companion/src/server/Uploader.js

@@ -543,7 +543,7 @@ class Uploader {
       logger.error(errMsg, 'upload.multipart.mismatch.error')
       logger.error(errMsg, 'upload.multipart.mismatch.error')
       this.emitError(new Error(errMsg))
       this.emitError(new Error(errMsg))
     } else {
     } else {
-      this.emitSuccess(null, { response: respObj })
+      this.emitSuccess(null, { response: respObj, bytesUploaded })
     }
     }
 
 
     this.cleanUp()
     this.cleanUp()

+ 1 - 1
packages/@uppy/core/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/core",
   "name": "@uppy/core",
   "description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
   "description": "Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "1.19.2",
+  "version": "1.20.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
   "style": "dist/style.min.css",

+ 60 - 2
packages/@uppy/core/src/index.js

@@ -22,6 +22,21 @@ class RestrictionError extends Error {
     this.isRestriction = true
     this.isRestriction = true
   }
   }
 }
 }
+if (typeof AggregateError === 'undefined') {
+  // eslint-disable-next-line no-global-assign
+  AggregateError = class AggregateError extends Error {
+    constructor (message, errors) {
+      super(message)
+      this.errors = errors
+    }
+  }
+}
+class AggregateRestrictionError extends AggregateError {
+  constructor (...args) {
+    super(...args)
+    this.isRestriction = true
+  }
+}
 
 
 /**
 /**
  * Uppy Core module.
  * Uppy Core module.
@@ -60,6 +75,8 @@ class Uppy {
           1: 'You have to select at least %{smart_count} files',
           1: 'You have to select at least %{smart_count} files',
         },
         },
         exceedsSize: '%{file} exceeds maximum allowed size of %{size}',
         exceedsSize: '%{file} exceeds maximum allowed size of %{size}',
+        missingRequiredMetaField: 'Missing required meta fields',
+        missingRequiredMetaFieldOnFile: 'Missing required meta fields in %{fileName}',
         inferiorSize: 'This file is smaller than the allowed size of %{size}',
         inferiorSize: 'This file is smaller than the allowed size of %{size}',
         youCanOnlyUploadFileTypes: 'You can only upload: %{types}',
         youCanOnlyUploadFileTypes: 'You can only upload: %{types}',
         noNewAlreadyUploading: 'Cannot add new files: already uploading',
         noNewAlreadyUploading: 'Cannot add new files: already uploading',
@@ -108,6 +125,7 @@ class Uppy {
         maxNumberOfFiles: null,
         maxNumberOfFiles: null,
         minNumberOfFiles: null,
         minNumberOfFiles: null,
         allowedFileTypes: null,
         allowedFileTypes: null,
+        requiredMetaFields: [],
       },
       },
       meta: {},
       meta: {},
       onBeforeFileAdded: (currentFile) => currentFile,
       onBeforeFileAdded: (currentFile) => currentFile,
@@ -239,8 +257,11 @@ class Uppy {
 
 
   /**
   /**
    * Back compat for when uppy.state is used instead of uppy.getState().
    * Back compat for when uppy.state is used instead of uppy.getState().
+   *
+   * @deprecated
    */
    */
   get state () {
   get state () {
+    // Here, state is a non-enumerable property.
     return this.getState()
     return this.getState()
   }
   }
 
 
@@ -535,6 +556,32 @@ class Uppy {
     }
     }
   }
   }
 
 
+  /**
+   * Check if requiredMetaField restriction is met before uploading.
+   *
+   */
+  #checkRequiredMetaFields (files) {
+    const { requiredMetaFields } = this.opts.restrictions
+    const { hasOwnProperty } = Object.prototype.hasOwnProperty
+
+    const errors = []
+    const fileIDs = Object.keys(files)
+    for (let i = 0; i < fileIDs.length; i++) {
+      const file = this.getFile(fileIDs[i])
+      for (let i = 0; i < requiredMetaFields.length; i++) {
+        if (!hasOwnProperty.call(file.meta, requiredMetaFields[i])) {
+          const err = new RestrictionError(`${this.i18n('missingRequiredMetaFieldOnFile', { fileName: file.name })}`)
+          errors.push(err)
+          this.showOrLogErrorAndThrow(err, { file, throwErr: false })
+        }
+      }
+    }
+
+    if (errors.length) {
+      throw new AggregateRestrictionError(`${this.i18n('missingRequiredMetaField')}`, errors)
+    }
+  }
+
   /**
   /**
    * Logs an error, sets Informer message, then throws the error.
    * Logs an error, sets Informer message, then throws the error.
    * Emits a 'restriction-failed' event if it’s a restriction error
    * Emits a 'restriction-failed' event if it’s a restriction error
@@ -1164,6 +1211,14 @@ class Uppy {
         isPaused: false,
         isPaused: false,
       })
       })
 
 
+      // Remote providers sometimes don't tell us the file size,
+      // but we can know how many bytes we uploaded once the upload is complete.
+      if (file.size == null) {
+        this.setFileState(file.id, {
+          size: uploadResp.bytesUploaded || currentProgress.bytesTotal,
+        })
+      }
+
       this.calculateTotalProgress()
       this.calculateTotalProgress()
     })
     })
 
 
@@ -1576,7 +1631,7 @@ class Uppy {
     // Not returning the `catch`ed promise, because we still want to return a rejected
     // Not returning the `catch`ed promise, because we still want to return a rejected
     // promise from this method if the upload failed.
     // promise from this method if the upload failed.
     lastStep.catch((err) => {
     lastStep.catch((err) => {
-      this.emit('error', err, uploadID)
+      this.emit('error', err)
       this.#removeUpload(uploadID)
       this.#removeUpload(uploadID)
     })
     })
 
 
@@ -1662,7 +1717,10 @@ class Uppy {
     }
     }
 
 
     return Promise.resolve()
     return Promise.resolve()
-      .then(() => this.#checkMinNumberOfFiles(files))
+      .then(() => {
+        this.#checkMinNumberOfFiles(files)
+        this.#checkRequiredMetaFields(files)
+      })
       .catch((err) => {
       .catch((err) => {
         this.#showOrLogErrorAndThrow(err)
         this.#showOrLogErrorAndThrow(err)
       })
       })

+ 1 - 1
packages/@uppy/core/src/index.test.js

@@ -1365,7 +1365,7 @@ describe('src/Core', () => {
       // wait for success event
       // wait for success event
       await finishPromise
       await finishPromise
 
 
-      expect(core.getFiles()[0].size).toBeNull()
+      expect(core.getFiles()[0].size).toBe(3456)
       expect(core.getFiles()[0].progress).toMatchObject({
       expect(core.getFiles()[0].progress).toMatchObject({
         bytesUploaded: 3456,
         bytesUploaded: 3456,
         bytesTotal: 3456,
         bytesTotal: 3456,

+ 1 - 1
packages/@uppy/dashboard/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/dashboard",
   "name": "@uppy/dashboard",
   "description": "Universal UI plugin for Uppy.",
   "description": "Universal UI plugin for Uppy.",
-  "version": "1.20.2",
+  "version": "1.21.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
   "style": "dist/style.min.css",

+ 8 - 1
packages/@uppy/dashboard/src/components/EditorPanel.js

@@ -22,7 +22,14 @@ function EditorPanel (props) {
           type="button"
           type="button"
           onClick={props.hideAllPanels}
           onClick={props.hideAllPanels}
         >
         >
-          {props.i18n('done')}
+          {props.i18n('cancel')}
+        </button>
+        <button
+          className="uppy-DashboardContent-save"
+          type="button"
+          onClick={props.saveFileEditor}
+        >
+          {props.i18n('save')}
         </button>
         </button>
       </div>
       </div>
       <div className="uppy-DashboardContent-panelBody">
       <div className="uppy-DashboardContent-panelBody">

+ 29 - 16
packages/@uppy/dashboard/src/components/FileCard/index.js

@@ -1,5 +1,6 @@
 const { h, Component } = require('preact')
 const { h, Component } = require('preact')
 const classNames = require('classnames')
 const classNames = require('classnames')
+const { nanoid } = require('nanoid')
 const getFileTypeIcon = require('../../utils/getFileTypeIcon')
 const getFileTypeIcon = require('../../utils/getFileTypeIcon')
 const ignoreEvent = require('../../utils/ignoreEvent.js')
 const ignoreEvent = require('../../utils/ignoreEvent.js')
 const FilePreview = require('../FilePreview')
 const FilePreview = require('../FilePreview')
@@ -19,15 +20,8 @@ class FileCard extends Component {
     this.state = {
     this.state = {
       formState: storedMetaData,
       formState: storedMetaData,
     }
     }
-  }
 
 
-  saveOnEnter = (ev) => {
-    if (ev.keyCode === 13) {
-      ev.stopPropagation()
-      ev.preventDefault()
-      const file = this.props.files[this.props.fileCardFor]
-      this.props.saveFileCard(this.state.formState, file.id)
-    }
+    this.form.id = nanoid()
   }
   }
 
 
   updateMeta = (newVal, name) => {
   updateMeta = (newVal, name) => {
@@ -39,7 +33,10 @@ class FileCard extends Component {
     }))
     }))
   }
   }
 
 
-  handleSave = () => {
+  form = document.createElement('form');
+
+  handleSave = (e) => {
+    e.preventDefault()
     const fileID = this.props.fileCardFor
     const fileID = this.props.fileCardFor
     this.props.saveFileCard(this.state.formState, fileID)
     this.props.saveFileCard(this.state.formState, fileID)
   }
   }
@@ -48,6 +45,17 @@ class FileCard extends Component {
     this.props.toggleFileCard(false)
     this.props.toggleFileCard(false)
   }
   }
 
 
+  // TODO(aduh95): move this to `UNSAFE_componentWillMount` when updating to Preact X+.
+  componentWillMount () {
+    this.form.addEventListener('submit', this.handleSave)
+    document.body.appendChild(this.form)
+  }
+
+  componentWillUnmount () {
+    this.form.removeEventListener('submit', this.handleSave)
+    document.body.removeChild(this.form)
+  }
+
   renderMetaFields = () => {
   renderMetaFields = () => {
     const metaFields = this.getMetaFields() || []
     const metaFields = this.getMetaFields() || []
     const fieldCSSClasses = {
     const fieldCSSClasses = {
@@ -56,6 +64,7 @@ class FileCard extends Component {
 
 
     return metaFields.map((field) => {
     return metaFields.map((field) => {
       const id = `uppy-Dashboard-FileCard-input-${field.id}`
       const id = `uppy-Dashboard-FileCard-input-${field.id}`
+      const required = this.props.requiredMetaFields.includes(field.id)
       return (
       return (
         <fieldset key={field.id} className="uppy-Dashboard-FileCard-fieldset">
         <fieldset key={field.id} className="uppy-Dashboard-FileCard-fieldset">
           <label className="uppy-Dashboard-FileCard-label" htmlFor={id}>{field.name}</label>
           <label className="uppy-Dashboard-FileCard-label" htmlFor={id}>{field.name}</label>
@@ -64,17 +73,18 @@ class FileCard extends Component {
               value: this.state.formState[field.id],
               value: this.state.formState[field.id],
               onChange: (newVal) => this.updateMeta(newVal, field.id),
               onChange: (newVal) => this.updateMeta(newVal, field.id),
               fieldCSSClasses,
               fieldCSSClasses,
+              required,
+              form: this.form.id,
             }, h)
             }, h)
             : (
             : (
               <input
               <input
                 className={fieldCSSClasses.text}
                 className={fieldCSSClasses.text}
                 id={id}
                 id={id}
+                form={this.form.id}
                 type={field.type || 'text'}
                 type={field.type || 'text'}
+                required={required}
                 value={this.state.formState[field.id]}
                 value={this.state.formState[field.id]}
                 placeholder={field.placeholder}
                 placeholder={field.placeholder}
-                onKeyUp={this.saveOnEnter}
-                onKeyDown={this.saveOnEnter}
-                onKeyPress={this.saveOnEnter}
                 onInput={ev => this.updateMeta(ev.target.value, field.id)}
                 onInput={ev => this.updateMeta(ev.target.value, field.id)}
                 data-uppy-super-focusable
                 data-uppy-super-focusable
               />
               />
@@ -112,10 +122,11 @@ class FileCard extends Component {
           <button
           <button
             className="uppy-DashboardContent-back"
             className="uppy-DashboardContent-back"
             type="button"
             type="button"
+            form={this.form.id}
             title={this.props.i18n('finishEditingFile')}
             title={this.props.i18n('finishEditingFile')}
-            onClick={this.handleSave}
+            onClick={this.handleCancel}
           >
           >
-            {this.props.i18n('done')}
+            {this.props.i18n('cancel')}
           </button>
           </button>
         </div>
         </div>
 
 
@@ -128,6 +139,7 @@ class FileCard extends Component {
                 type="button"
                 type="button"
                 className="uppy-u-reset uppy-c-btn uppy-Dashboard-FileCard-edit"
                 className="uppy-u-reset uppy-c-btn uppy-Dashboard-FileCard-edit"
                 onClick={() => this.props.openFileEditor(file)}
                 onClick={() => this.props.openFileEditor(file)}
+                form={this.form.id}
               >
               >
                 {this.props.i18n('editFile')}
                 {this.props.i18n('editFile')}
               </button>
               </button>
@@ -141,8 +153,8 @@ class FileCard extends Component {
           <div className="uppy-Dashboard-FileCard-actions">
           <div className="uppy-Dashboard-FileCard-actions">
             <button
             <button
               className="uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-FileCard-actionsBtn"
               className="uppy-u-reset uppy-c-btn uppy-c-btn-primary uppy-Dashboard-FileCard-actionsBtn"
-              type="button"
-              onClick={this.handleSave}
+              type="submit"
+              form={this.form.id}
             >
             >
               {this.props.i18n('saveChanges')}
               {this.props.i18n('saveChanges')}
             </button>
             </button>
@@ -150,6 +162,7 @@ class FileCard extends Component {
               className="uppy-u-reset uppy-c-btn uppy-c-btn-link uppy-Dashboard-FileCard-actionsBtn"
               className="uppy-u-reset uppy-c-btn uppy-c-btn-link uppy-Dashboard-FileCard-actionsBtn"
               type="button"
               type="button"
               onClick={this.handleCancel}
               onClick={this.handleCancel}
+              form={this.form.id}
             >
             >
               {this.props.i18n('cancel')}
               {this.props.i18n('cancel')}
             </button>
             </button>

+ 1 - 1
packages/@uppy/dashboard/src/components/PickerPanelContent.js

@@ -23,7 +23,7 @@ function PickerPanelContent (props) {
           type="button"
           type="button"
           onClick={props.hideAllPanels}
           onClick={props.hideAllPanels}
         >
         >
-          {props.i18n('done')}
+          {props.i18n('cancel')}
         </button>
         </button>
       </div>
       </div>
       <div className="uppy-DashboardContent-panelBody">
       <div className="uppy-DashboardContent-panelBody">

+ 14 - 1
packages/@uppy/dashboard/src/index.js

@@ -60,13 +60,13 @@ module.exports = class Dashboard extends UIPlugin {
         copyLinkToClipboardFallback: 'Copy the URL below',
         copyLinkToClipboardFallback: 'Copy the URL below',
         copyLink: 'Copy link',
         copyLink: 'Copy link',
         fileSource: 'File source: %{name}',
         fileSource: 'File source: %{name}',
-        done: 'Done',
         back: 'Back',
         back: 'Back',
         addMore: 'Add more',
         addMore: 'Add more',
         removeFile: 'Remove file',
         removeFile: 'Remove file',
         editFile: 'Edit file',
         editFile: 'Edit file',
         editing: 'Editing %{file}',
         editing: 'Editing %{file}',
         finishEditingFile: 'Finish editing file',
         finishEditingFile: 'Finish editing file',
+        save: 'Save',
         saveChanges: 'Save changes',
         saveChanges: 'Save changes',
         cancel: 'Cancel',
         cancel: 'Cancel',
         myDevice: 'My Device',
         myDevice: 'My Device',
@@ -275,6 +275,17 @@ module.exports = class Dashboard extends UIPlugin {
     })
     })
   }
   }
 
 
+  saveFileEditor = () => {
+    const { targets } = this.getPluginState()
+    const editors = this._getEditors(targets)
+
+    editors.forEach((editor) => {
+      this.uppy.getPlugin(editor.id).save()
+    })
+
+    this.hideAllPanels()
+  }
+
   openModal = () => {
   openModal = () => {
     const { promise, resolve } = createPromise()
     const { promise, resolve } = createPromise()
     // save scroll position
     // save scroll position
@@ -920,6 +931,7 @@ module.exports = class Dashboard extends UIPlugin {
       direction: this.opts.direction,
       direction: this.opts.direction,
       activePickerPanel: pluginState.activePickerPanel,
       activePickerPanel: pluginState.activePickerPanel,
       showFileEditor: pluginState.showFileEditor,
       showFileEditor: pluginState.showFileEditor,
+      saveFileEditor: this.saveFileEditor,
       disableAllFocusableElements: this.disableAllFocusableElements,
       disableAllFocusableElements: this.disableAllFocusableElements,
       animateOpenClose: this.opts.animateOpenClose,
       animateOpenClose: this.opts.animateOpenClose,
       isClosing: pluginState.isClosing,
       isClosing: pluginState.isClosing,
@@ -974,6 +986,7 @@ module.exports = class Dashboard extends UIPlugin {
       parentElement: this.el,
       parentElement: this.el,
       allowedFileTypes: this.uppy.opts.restrictions.allowedFileTypes,
       allowedFileTypes: this.uppy.opts.restrictions.allowedFileTypes,
       maxNumberOfFiles: this.uppy.opts.restrictions.maxNumberOfFiles,
       maxNumberOfFiles: this.uppy.opts.restrictions.maxNumberOfFiles,
+      requiredMetaFields: this.uppy.opts.restrictions.requiredMetaFields,
       showSelectedFiles: this.opts.showSelectedFiles,
       showSelectedFiles: this.opts.showSelectedFiles,
       handleCancelRestore: this.handleCancelRestore,
       handleCancelRestore: this.handleCancelRestore,
       handleRequestThumbnail: this.handleRequestThumbnail,
       handleRequestThumbnail: this.handleRequestThumbnail,

+ 2 - 1
packages/@uppy/dashboard/src/style.scss

@@ -549,7 +549,8 @@
     }
     }
   }
   }
 
 
-  .uppy-DashboardContent-back {
+  .uppy-DashboardContent-back,
+  .uppy-DashboardContent-save {
     @include reset-button;
     @include reset-button;
     @include highlight-focus;
     @include highlight-focus;
     border-radius: 3px;
     border-radius: 3px;

+ 2 - 0
packages/@uppy/dashboard/types/index.d.ts

@@ -5,6 +5,8 @@ import DashboardLocale from './generatedLocale'
 type FieldRenderOptions = {
 type FieldRenderOptions = {
   value: string,
   value: string,
   onChange: (newVal: string) => void
   onChange: (newVal: string) => void
+  fieldCSSClasses: { text: string }
+  required?: boolean
 }
 }
 
 
 type PreactRender = (node: any, params: Record<string, unknown> | null, ...children: any[]) => any
 type PreactRender = (node: any, params: Record<string, unknown> | null, ...children: any[]) => any

+ 1 - 1
packages/@uppy/drag-drop/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/drag-drop",
   "name": "@uppy/drag-drop",
   "description": "Droppable zone UI for Uppy. Drag and drop files into it to upload.",
   "description": "Droppable zone UI for Uppy. Drag and drop files into it to upload.",
-  "version": "1.4.30",
+  "version": "1.4.31",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/image-editor/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/image-editor",
   "name": "@uppy/image-editor",
   "description": "Image editor and cropping UI",
   "description": "Image editor and cropping UI",
-  "version": "0.3.0",
+  "version": "0.4.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
   "style": "dist/style.min.css",

+ 5 - 25
packages/@uppy/image-editor/src/Editor.js

@@ -8,11 +8,14 @@ module.exports = class Editor extends Component {
   }
   }
 
 
   componentDidMount () {
   componentDidMount () {
+    const { opts, storeCropperInstance } = this.props
     this.cropper = new Cropper(
     this.cropper = new Cropper(
       this.imgElement,
       this.imgElement,
-      this.props.opts.cropperOptions
+      opts.cropperOptions
     )
     )
-    if (this.props.opts.actions.granularRotate) {
+    storeCropperInstance(this.cropper)
+
+    if (opts.actions.granularRotate) {
       this.imgElement.addEventListener('crop', (ev) => {
       this.imgElement.addEventListener('crop', (ev) => {
         const rotationAngle = ev.detail.rotate
         const rotationAngle = ev.detail.rotate
         this.setState({
         this.setState({
@@ -28,15 +31,6 @@ module.exports = class Editor extends Component {
     this.cropper.destroy()
     this.cropper.destroy()
   }
   }
 
 
-  save = () => {
-    this.cropper.getCroppedCanvas()
-      .toBlob(
-        (blob) => this.props.save(blob),
-        this.props.currentImage.type,
-        this.props.opts.quality
-      )
-  }
-
   renderRevert () {
   renderRevert () {
     return (
     return (
       <button
       <button
@@ -236,20 +230,6 @@ module.exports = class Editor extends Component {
         </div>
         </div>
 
 
         <div className="uppy-ImageCropper-controls">
         <div className="uppy-ImageCropper-controls">
-          <button
-            type="button"
-            className="uppy-u-reset uppy-c-btn"
-            aria-label={i18n('save')}
-            data-microtip-position="top"
-            role="tooltip"
-            onClick={() => this.save()}
-          >
-            <svg aria-hidden="true" className="uppy-c-icon" width="24" height="24" viewBox="0 0 24 24">
-              <path d="M0 0h24v24H0z" fill="none" />
-              <path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
-            </svg>
-          </button>
-
           {actions.revert && this.renderRevert()}
           {actions.revert && this.renderRevert()}
           {actions.rotate && this.renderRotate()}
           {actions.rotate && this.renderRotate()}
           {actions.granularRotate && this.renderGranularRotate()}
           {actions.granularRotate && this.renderGranularRotate()}

+ 27 - 13
packages/@uppy/image-editor/src/index.js

@@ -14,7 +14,6 @@ module.exports = class ImageEditor extends UIPlugin {
 
 
     this.defaultLocale = {
     this.defaultLocale = {
       strings: {
       strings: {
-        save: 'Save',
         revert: 'Revert',
         revert: 'Revert',
         rotate: 'Rotate',
         rotate: 'Rotate',
         zoomIn: 'Zoom in',
         zoomIn: 'Zoom in',
@@ -91,21 +90,35 @@ module.exports = class ImageEditor extends UIPlugin {
     return false
     return false
   }
   }
 
 
-  save = (blob) => {
+  save = () => {
+    const saveBlobCallback = (blob) => {
+      const { currentImage } = this.getPluginState()
+
+      this.uppy.setFileState(currentImage.id, {
+        data: blob,
+        size: blob.size,
+        preview: null,
+      })
+
+      const updatedFile = this.uppy.getFile(currentImage.id)
+      this.uppy.emit('thumbnail:request', updatedFile)
+      this.setPluginState({
+        currentImage: updatedFile,
+      })
+      this.uppy.emit('file-editor:complete', updatedFile)
+    }
+
     const { currentImage } = this.getPluginState()
     const { currentImage } = this.getPluginState()
 
 
-    this.uppy.setFileState(currentImage.id, {
-      data: blob,
-      size: blob.size,
-      preview: null,
-    })
+    this.cropper.getCroppedCanvas().toBlob(
+      saveBlobCallback,
+      currentImage.type,
+      this.opts.quality
+    )
+  }
 
 
-    const updatedFile = this.uppy.getFile(currentImage.id)
-    this.uppy.emit('thumbnail:request', updatedFile)
-    this.setPluginState({
-      currentImage: updatedFile,
-    })
-    this.uppy.emit('file-editor:complete', updatedFile)
+  storeCropperInstance = (cropper) => {
+    this.cropper = cropper
   }
   }
 
 
   selectFile = (file) => {
   selectFile = (file) => {
@@ -139,6 +152,7 @@ module.exports = class ImageEditor extends UIPlugin {
     return (
     return (
       <Editor
       <Editor
         currentImage={currentImage}
         currentImage={currentImage}
+        storeCropperInstance={this.storeCropperInstance}
         save={this.save}
         save={this.save}
         opts={this.opts}
         opts={this.opts}
         i18n={this.i18n}
         i18n={this.i18n}

+ 1 - 1
packages/@uppy/locales/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/locales",
   "name": "@uppy/locales",
   "description": "Uppy language packs",
   "description": "Uppy language packs",
-  "version": "1.21.0",
+  "version": "1.22.0",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [
     "uppy",
     "uppy",

+ 1 - 1
packages/@uppy/locales/src/de_DE.js

@@ -110,7 +110,7 @@ de_DE.strings = {
     '1': '+%{smart_count} Dateien hochladen',
     '1': '+%{smart_count} Dateien hochladen',
     '2': '+%{smart_count} Dateien hochladen',
     '2': '+%{smart_count} Dateien hochladen',
   },
   },
-  uploading: 'Uploading',
+  uploading: 'Wird hochgeladen',
   uploadingXFiles: {
   uploadingXFiles: {
     '0': '%{smart_count} Datei wird hochgeladen',
     '0': '%{smart_count} Datei wird hochgeladen',
     '1': '%{smart_count} Dateien werden hochgeladen',
     '1': '%{smart_count} Dateien werden hochgeladen',

+ 2 - 0
packages/@uppy/locales/src/en_US.js

@@ -78,6 +78,8 @@ en_US.strings = {
   loading: 'Loading...',
   loading: 'Loading...',
   logOut: 'Log out',
   logOut: 'Log out',
   micDisabled: 'Microphone access denied by user',
   micDisabled: 'Microphone access denied by user',
+  missingRequiredMetaField: 'Missing required meta fields',
+  missingRequiredMetaFieldOnFile: 'Missing required meta fields in %{fileName}',
   myDevice: 'My Device',
   myDevice: 'My Device',
   noCameraDescription: 'In order to take pictures or record video, please connect a camera device',
   noCameraDescription: 'In order to take pictures or record video, please connect a camera device',
   noCameraTitle: 'Camera Not Available',
   noCameraTitle: 'Camera Not Available',

+ 1 - 1
packages/@uppy/react/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/react",
   "name": "@uppy/react",
   "description": "React component wrappers around Uppy's official UI plugins.",
   "description": "React component wrappers around Uppy's official UI plugins.",
-  "version": "1.12.0",
+  "version": "1.12.1",
   "license": "MIT",
   "license": "MIT",
   "main": "index.js",
   "main": "index.js",
   "module": "index.mjs",
   "module": "index.mjs",

+ 2 - 2
packages/@uppy/robodog/README.md

@@ -18,8 +18,8 @@ We recommend installing from npm and then using a module bundler such as [Webpac
 Alternatively, you can also use this package in a pre-built bundle from Transloadit's CDN: Edgly.
 Alternatively, you can also use this package in a pre-built bundle from Transloadit's CDN: Edgly.
 
 
 ```html
 ```html
-<link rel="stylesheet" href="https://releases.transloadit.com/uppy/v1.30.0/robodog.min.css">
-<script src="https://releases.transloadit.com/uppy/v1.30.0/robodog.min.js"></script>
+<link rel="stylesheet" href="https://releases.transloadit.com/uppy/v1.31.0/robodog.min.css">
+<script src="https://releases.transloadit.com/uppy/v1.31.0/robodog.min.js"></script>
 ```
 ```
 
 
 Then, a global `Robodog` variable will be available. For usage instructions, please see the [main Robodog documentation](https://uppy.io/docs/robodog).
 Then, a global `Robodog` variable will be available. For usage instructions, please see the [main Robodog documentation](https://uppy.io/docs/robodog).

+ 1 - 1
packages/@uppy/robodog/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/robodog",
   "name": "@uppy/robodog",
   "description": "Transloadit SDK for browsers based on Uppy",
   "description": "Transloadit SDK for browsers based on Uppy",
-  "version": "1.10.12",
+  "version": "1.11.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "jsnext:main": "src/index.js",
   "jsnext:main": "src/index.js",

+ 55 - 0
packages/@uppy/robodog/types/index.d.ts

@@ -0,0 +1,55 @@
+import Uppy = require('@uppy/core');
+import Transloadit = require('@uppy/transloadit')
+import Dashboard = require('@uppy/dashboard')
+import Dropbox = require('@uppy/dropbox')
+import GoogleDrive = require('@uppy/google-drive')
+import Instagram = require('@uppy/instagram');
+import Url = require('@uppy/url')
+import Webcam = require('@uppy/webcam')
+import Onedrive = require('@uppy/onedrive')
+import Facebook = require('@uppy/facebook');
+import Form = require('@uppy/form')
+
+declare namespace Robodog {
+    type Provider = 'dropbox' | 'google-drive' | 'instagram' | 'url' | 'webcam' | 'onedrive' | 'facebook'
+
+    interface RobodogOptionsBase extends Uppy.UppyOptions {
+        providers?: Provider[]
+        companionUrl?: string,
+        companionAllowedHosts?: string | RegExp | Array<string | RegExp>
+        companionHeaders?: Record<string, string>,
+        dropbox?: Dropbox.DropboxOptions
+        googleDrive?: GoogleDrive.GoogleDriveOptions
+        instagram?: Instagram.InstagramOptions
+        url?: Url.UrlOptions
+        webcam?: Webcam.WebcamOptions,
+        onedrive?: Onedrive.OneDriveOptions,
+        facebook?: Facebook.FacebookOptions
+    }
+
+    type RobodogOptions = RobodogOptionsBase & Transloadit.TransloaditOptions & Dashboard.DashboardOptions
+
+    interface RobodogTransloaditResult extends Transloadit.Result {
+        assemblyId: string,
+        stepName: string
+    }
+
+    interface RobodogResult extends Uppy.UploadResult {
+        transloadit: Transloadit.Assembly[],
+        results?: RobodogTransloaditResult[]
+    }
+
+    function pick(opts: RobodogOptions): Promise<RobodogResult>;
+
+    type RobodogFormOptions = RobodogOptions
+        & Pick<Form.FormOptions, 'submitOnSuccess' | 'triggerUploadOnSubmit'>
+        & { modal?: boolean, statusbar?: string }
+
+    function form(target: string, opts: RobodogFormOptions): Uppy.Uppy
+
+    function upload(files: (File | Blob & { name: string })[], opts: RobodogOptions): Promise<RobodogResult>;
+
+    function dashboard(target: string, opts: RobodogOptions): Uppy.Uppy;
+}
+
+export = Robodog;

+ 83 - 0
packages/@uppy/robodog/types/index.test-d.ts

@@ -0,0 +1,83 @@
+import { Transloadit } from 'uppy' // eslint-disable-line import/no-extraneous-dependencies
+import { expectError } from 'tsd'
+import Robodog from '.'
+
+async function performPick () {
+  const { successful, failed, transloadit, results } = await Robodog.pick({
+    target: 'test',
+    errorReporting: true,
+    waitForEncoding: false,
+    waitForMetadata: false,
+    animateOpenClose: true,
+    inline: false,
+    params: {
+      auth: { key: '' },
+      template_id: '',
+    },
+    providers: ['webcam', 'url'],
+    webcam: {
+      countdown: false,
+      modes: [
+        'video-audio',
+        'video-only',
+        'audio-only',
+        'picture',
+      ],
+      mirror: true,
+    },
+    url: {
+      companionUrl: Transloadit.COMPANION,
+    },
+  })
+}
+
+const instance = Robodog.form('string', {
+  submitOnSuccess: true,
+  triggerUploadOnSubmit: false,
+  params: {
+    auth: { key: '' },
+    template_id: '',
+  },
+  modal: true,
+  closeAfterFinish: true,
+  statusbar: 'target',
+})
+
+// should not have access to omitted form settings
+expectError(Robodog.form('string', {
+  addResultToForm: false,
+}))
+
+// target is required
+expectError(Robodog.form({
+  addResultToForm: false,
+}))
+
+const files: File[] = []
+
+const upload = Robodog.upload(files, {
+  debug: true,
+  errorReporting: true,
+  params: {
+    auth: { key: '' },
+    template_id: '',
+  },
+})
+
+// Files array is required
+expectError(Robodog.upload({ debug: true }))
+
+const dashboard = Robodog.dashboard('selector', {
+  debug: true,
+  errorReporting: true,
+  params: {
+    auth: { key: '' },
+    template_id: '',
+  },
+})
+  .on('transloadit:result', (result) => {
+    console.log(result)
+  })
+
+// selector is required
+expectError(Robodog.dashboard({ }))

+ 1 - 1
packages/@uppy/screen-capture/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/screen-capture",
   "name": "@uppy/screen-capture",
   "description": "Uppy plugin that captures video from display or application.",
   "description": "Uppy plugin that captures video from display or application.",
-  "version": "1.0.21",
+  "version": "1.1.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
   "style": "dist/style.min.css",

+ 1 - 1
packages/@uppy/svelte/package.json

@@ -3,7 +3,7 @@
   "svelte": "src/index.js",
   "svelte": "src/index.js",
   "module": "dist/index.mjs",
   "module": "dist/index.mjs",
   "main": "dist/index.js",
   "main": "dist/index.js",
-  "version": "0.1.12",
+  "version": "0.1.13",
   "scripts": {
   "scripts": {
     "build": "rollup -c",
     "build": "rollup -c",
     "prepublishOnly": "npm run build",
     "prepublishOnly": "npm run build",

+ 1 - 1
packages/@uppy/transloadit/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/transloadit",
   "name": "@uppy/transloadit",
   "description": "The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more",
   "description": "The Transloadit plugin can be used to upload files to Transloadit for all kinds of processing, such as transcoding video, resizing images, zipping/unzipping, and more",
-  "version": "1.6.26",
+  "version": "1.7.0",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
   "types": "types/index.d.ts",

+ 84 - 1
packages/@uppy/transloadit/types/index.d.ts

@@ -1,7 +1,90 @@
 import type { PluginOptions, UppyFile, BasePlugin } from '@uppy/core'
 import type { PluginOptions, UppyFile, BasePlugin } from '@uppy/core'
 import TransloaditLocale from './generatedLocale'
 import TransloaditLocale from './generatedLocale'
 
 
-interface AssemblyParameters {
+  interface FileInfo {
+    id: string,
+    name: string,
+    basename: string,
+    ext: string,
+    size: number,
+    mime: string,
+    type: string,
+    field: string,
+    md5hash: string,
+    is_tus_file: boolean,
+    original_md5hash: string,
+    original_id: string,
+    original_name: string
+    original_basename: string,
+    original_path: string,
+    url: string,
+    ssl_url: string,
+    tus_upload_url: string,
+    meta: Record<string, any>
+  }
+
+  interface Result extends FileInfo {
+    cost: number,
+    execTime: number,
+    queue: string,
+    queueTime: number
+  }
+
+export interface Assembly {
+    ok?: string,
+    message?: string,
+    assembly_id: string,
+    parent_id?: string,
+    account_id: string,
+    template_id?: string,
+    instance: string,
+    assembly_url: string,
+    assembly_ssl_url: string,
+    uppyserver_url: string,
+    companion_url: string,
+    websocket_url: string,
+    tus_url: string,
+    bytes_received: number,
+    bytes_expected: number,
+    upload_duration: number,
+    client_agent?: string,
+    client_ip?: string,
+    client_referer?: string,
+    transloadit_client: string,
+    start_date: string,
+    upload_meta_data_extracted: boolean,
+    warnings: any[],
+    is_infinite: boolean,
+    has_dupe_jobs: boolean,
+    execution_start: string,
+    execution_duration: number,
+    queue_duration: number,
+    jobs_queue_duration: number,
+    notify_start?: any,
+    notify_url?: string,
+    notify_status?: any,
+    notify_response_code?: any,
+    notify_duration?: any,
+    last_job_completed?: string,
+    fields: Record<string, any>,
+    running_jobs: any[],
+    bytes_usage: number,
+    executing_jobs: any[],
+    started_jobs: string[],
+    parent_assembly_status: any,
+    params: string,
+    template?: any,
+    merged_params: string,
+    uploads: FileInfo[],
+    results: Record<string, Result[]>,
+    build_id: string,
+    error?: string,
+    stderr?: string,
+    stdout?: string,
+    reason?: string,
+  }
+
+  interface AssemblyParameters {
     auth: {
     auth: {
       key: string,
       key: string,
       expires?: string
       expires?: string

+ 1 - 1
packages/@uppy/vue/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@uppy/vue",
   "name": "@uppy/vue",
-  "version": "0.2.5",
+  "version": "0.2.6",
   "private": false,
   "private": false,
   "main": "lib/index.js",
   "main": "lib/index.js",
   "types": "types/index.d.ts",
   "types": "types/index.d.ts",

+ 1 - 1
packages/@uppy/webcam/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@uppy/webcam",
   "name": "@uppy/webcam",
   "description": "Uppy plugin that takes photos or records videos using the device's camera.",
   "description": "Uppy plugin that takes photos or records videos using the device's camera.",
-  "version": "1.8.12",
+  "version": "1.8.13",
   "license": "MIT",
   "license": "MIT",
   "main": "lib/index.js",
   "main": "lib/index.js",
   "style": "dist/style.min.css",
   "style": "dist/style.min.css",

+ 1 - 1
packages/uppy/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "uppy",
   "name": "uppy",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
   "description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
-  "version": "1.30.0",
+  "version": "1.31.0",
   "license": "MIT",
   "license": "MIT",
   "main": "index.js",
   "main": "index.js",
   "module": "index.mjs",
   "module": "index.mjs",

+ 2 - 0
test/endtoend/providers/main.js

@@ -3,6 +3,7 @@ const Dashboard = require('@uppy/dashboard')
 const GoogleDrive = require('@uppy/google-drive')
 const GoogleDrive = require('@uppy/google-drive')
 const Instagram = require('@uppy/instagram')
 const Instagram = require('@uppy/instagram')
 const Dropbox = require('@uppy/dropbox')
 const Dropbox = require('@uppy/dropbox')
+const Box = require('@uppy/box')
 const Tus = require('@uppy/tus')
 const Tus = require('@uppy/tus')
 
 
 const isOnTravis = !!(process.env.TRAVIS && process.env.CI)
 const isOnTravis = !!(process.env.TRAVIS && process.env.CI)
@@ -20,6 +21,7 @@ window.uppy = new Uppy({
   .use(GoogleDrive, { target: Dashboard, companionUrl })
   .use(GoogleDrive, { target: Dashboard, companionUrl })
   .use(Instagram, { target: Dashboard, companionUrl })
   .use(Instagram, { target: Dashboard, companionUrl })
   .use(Dropbox, { target: Dashboard, companionUrl })
   .use(Dropbox, { target: Dashboard, companionUrl })
+  .use(Box, { target: Dashboard, companionUrl })
   .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
   .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
 
 
 if (window.location.search === '?socketerr=true') {
 if (window.location.search === '?socketerr=true') {

+ 2 - 0
test/endtoend/typescript/main.ts

@@ -4,6 +4,7 @@ import {
   Instagram,
   Instagram,
   Dropbox,
   Dropbox,
   GoogleDrive,
   GoogleDrive,
+  Box,
   Url,
   Url,
   Webcam,
   Webcam,
   Tus,
   Tus,
@@ -36,6 +37,7 @@ const uppy = new Core({
   .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(GoogleDrive, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Instagram, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Dropbox, { target: Dashboard, companionUrl: 'http://localhost:3020' })
+  .use(Box, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Url, { target: Dashboard, companionUrl: 'http://localhost:3020' })
   .use(Webcam, { target: Dashboard })
   .use(Webcam, { target: Dashboard })
   .use(Tus, { endpoint: TUS_ENDPOINT })
   .use(Tus, { endpoint: TUS_ENDPOINT })

+ 102 - 51
website/src/_template/contributing.md

@@ -12,12 +12,12 @@ cd uppy
 npm install
 npm install
 ```
 ```
 
 
-Our websites examples section is also our playground, please read the [Local Previews](#Local-previews) section to get up and running.
+Our website's examples section is also our playground, please read the [Local Previews](#Local-previews) section to get up and running.
 
 
 ### Requiring files
 ### Requiring files
 
 
-- If we are `require()`ing a file from the same subpackage (e.g. require `@uppy/dashboard/utils/hi.js` from `@uppy/dashboard/src/index.js`) - we can freely use relative imports, as long as the required file is under the `src` directory (`/:packageName/src/**/*.js`).
-- But if we want to require some file from another subpackage - we should use global @uppy requires, and they should always be in the form of `@uppy/:packageName/(lib instead of src)/(same path).js`
+*   If we are `require()`ing a file from the same subpackage, we can freely use relative imports as long as the required file is under the `src` directory (e.g. to import `@uppy/dashboard/src/utils/hi.js` from `@uppy/dashboard/src/index.js`, use `require('./utils/hi.js')`).
+*   But if we want to `require()` some file from another subpackage - we should use global @uppy requires, and they should always be in the form of `@uppy/:packageName/(lib instead of src)/(same path).js`
 
 
 ## Tests
 ## Tests
 
 
@@ -27,7 +27,7 @@ Unit tests are using Jest and can be run with:
 npm run test:unit
 npm run test:unit
 ```
 ```
 
 
-For end-to-end tests, we use [Webdriverio](http://webdriver.io). For it to run locally, you need to install a Selenium standalone server. Just follow [the guide](http://webdriver.io/guide.html) to do so. You can also install a Selenium standalone server from NPM:
+For end-to-end tests, we use [Webdriverio](http://webdriver.io). For it to run locally, you need to install a Selenium standalone server. Follow [the Webdriverio guide](http://webdriver.io/guide.html) to do so. You can also install a Selenium standalone server from NPM:
 
 
 ```bash
 ```bash
 npm install selenium-standalone -g
 npm install selenium-standalone -g
@@ -54,13 +54,13 @@ npm run test:endtoend:local -- -b chrome
 
 
 > Note: The `--` is important, it tells npm that the remaining arguments should be interpreted by the script itself, not by npm.
 > Note: The `--` is important, it tells npm that the remaining arguments should be interpreted by the script itself, not by npm.
 
 
-You can run in multiple browsers by passing multiple `-b` flags:
+You can run in several browsers by passing several `-b` flags:
 
 
 ```bash
 ```bash
 npm run test:endtoend:local -- -b chrome -b firefox
 npm run test:endtoend:local -- -b chrome -b firefox
 ```
 ```
 
 
-When trying to get a specific integration test to pass, it's not that helpful to continuously run _all_ tests. You can use the `--suite` flag to run tests from a single `./test/endtoend` folder. For example, `--suite thumbnails` will only run the tests from `./test/endtoend/thumbnails`. Of course, it can also be combined with one or more `-b` flags.
+When trying to get a specific integration test to pass, its not that helpful to continuously run _all_ tests. You can use the `--suite` flag to run tests from a single `./test/endtoend` folder. For example, `--suite thumbnails` will only run the tests from `./test/endtoend/thumbnails`. It can also be joint with one or more `-b` flags.
 
 
 ```bash
 ```bash
 npm run test:endtoend:local -- -b chrome --suite thumbnails
 npm run test:endtoend:local -- -b chrome --suite thumbnails
@@ -68,6 +68,55 @@ npm run test:endtoend:local -- -b chrome --suite thumbnails
 
 
 These tests are also run automatically on Travis builds with [SauceLabs](https://saucelabs.com/) cloud service using different OSes.
 These tests are also run automatically on Travis builds with [SauceLabs](https://saucelabs.com/) cloud service using different OSes.
 
 
+## Development
+
+### Instagram integration
+
+Even though facebook [allows using](https://developers.facebook.com/blog/post/2018/06/08/enforce-https-facebook-login/) http://localhost in dev mode, Instagram doesn’t seem to support that, and seems to need a publically available domain name with HTTPS.
+
+Make sure that you are using a development facebook app at <https://developers.facebook.com/apps>
+
+Go to “Instagram Basic Display” and find `Instagram App ID` and `Instagram App Secret`. Put them in a file called `env.sh` in the repo root:
+
+```bash
+export COMPANION_INSTAGRAM_KEY="Instagram App ID"
+export COMPANION_INSTAGRAM_SECRET="Instagram App Secret"
+```
+
+Run
+
+```bash
+ngrok http 3020
+```
+
+Note the ngrok https base URL, e.g. `https://e0c7de09808d.ngrok.io` and
+append `/instagram/redirect` to it, e.g.:
+
+    https://e0c7de09808d.ngrok.io/instagram/redirect
+
+Add this full ngrok URL to `Valid OAuth Redirect URIs` under `Instagram Basic Display`.
+
+Edit `bin/companion` and change to your ngrok URI:
+
+```bash
+COMPANION_DOMAIN="e0c7de09808d.ngrok.io"
+COMPANION_PROTOCOL="https"
+```
+
+Edit `examples/dev/Dashboard.js`:
+
+```js
+const COMPANION_URL = 'https://e0c7de09808d.ngrok.io'
+```
+
+Go to: Roles -> Roles -> Add Instagram testers -> Add your instagram account
+
+Go to your instagram account at <https://www.instagram.com/accounts/manage_access/>
+
+Tester invites -> Accept
+
+Now you should be able to test the Instagram integration.
+
 ## Releases
 ## Releases
 
 
 Before doing a release, check that the examples on the website work:
 Before doing a release, check that the examples on the website work:
@@ -91,37 +140,37 @@ Releases are managed by [Lerna](https://github.com/lerna/lerna). We do some clea
 npm run release
 npm run release
 ```
 ```
 
 
-If you have two-factor authentication enabled on your account, Lerna will ask for a one-time password. There is an issue with the CLI where the OTP prompt may be obscured by a publishing progress bar. If Lerna appears to hang just as it starts publishing, chances are it's waiting for the password. Try typing in your OTP and hitting enter.
+If you have two-factor authentication enabled on your account, Lerna will ask for a one-time password. You may stumble upon a known issue with the CLI where the OTP prompt may be obscured by a publishing progress bar. If Lerna appears to freeze as it starts publishing, chances are it’s waiting for the password. Try typing in your OTP and hitting enter.
 
 
 Other things to keep in mind during release:
 Other things to keep in mind during release:
 
 
-* When doing a major release >= 1.0, of the `@uppy/core` package, the `peerDependency` of the plugin packages needs to be updated first. Eg when updating from 1.y.z to 2.0.0, the peerDependency of each should be `"@uppy/core": "^2.0.0"` before doing `npm run release`.
-* When adding a new package, add the following key to its package.json:
-  ```json
-  "publishConfig": { "access": "public" }
-  ```
-  Else, npm will try and fail to publish a _private_ package, because the `@uppy` scope on npm does not support that.
+*   When doing a major release >= 1.0, of the `@uppy/core` package, the `peerDependency` of the plugin packages needs to be updated first. Eg when updating from 1.y.z to 2.0.0, the peerDependency of each should be `"@uppy/core": "^2.0.0"` before doing `npm run release`.
+*   When adding a new package, add the following key to its package.json:
+    ```json
+    "publishConfig": { "access": "public" }
+    ```
+    Else, npm will try and fail to publish a _private_ package, because the `@uppy` scope on npm does not support that.
 
 
 After a release, the demos on transloadit.com should also be updated. After updating, check that some things work locally:
 After a release, the demos on transloadit.com should also be updated. After updating, check that some things work locally:
 
 
- - the demos in the demo section work (try one that uses an import robot, and one that you need to upload to)
- - the demos on the homepage work and can import from Google Drive, Instagram, Dropbox, etc.
+*   the demos in the demo section work (try one that uses an import robot, and one that you need to upload to)
+*   the demos on the homepage work and can import from Google Drive, Instagram, Dropbox, etc.
 
 
-If you don't have access to the transloadit.com source code ping @arturi or @goto-bus-stop and we'll pick it up. :sparkles:
+If you don’t have access to the transloadit.com source code ping @arturi or @goto-bus-stop and we’ll pick it up. :sparkles:
 
 
 ## Website development
 ## Website development
 
 
-We keep the [uppy.io](http://uppy.io) website in `./website`, so it’s easy to keep docs and code in sync as we are still iterating at high velocity.
+We keep the [uppy.io](http://uppy.io) website in `./website` to keep docs and code in sync as we are still iterating at high velocity.
 
 
-The site is built with [Hexo](http://hexo.io/), and Travis automatically deploys this onto GitHub Pages (it overwrites the `gh-pages` branch with Hexo's build at every change to `master`). The content is written in Markdown and located in `./website/src`. Feel free to fork & hack!
+The site is built with [Hexo](http://hexo.io/), and Travis automatically deploys this onto GitHub Pages (it overwrites the `gh-pages` branch with Hexos build at every change to `master`). The content is written in Markdown and located in `./website/src`. Feel free to fork & hack!
 
 
 Even though bundled in this repo, the website is regarded as a separate project. As such, it has its own `package.json` and we aim to keep the surface where the two projects interface as small as possible. `./website/update.js` is called during website builds to inject the Uppy knowledge into the site.
 Even though bundled in this repo, the website is regarded as a separate project. As such, it has its own `package.json` and we aim to keep the surface where the two projects interface as small as possible. `./website/update.js` is called during website builds to inject the Uppy knowledge into the site.
 
 
 ### Local previews
 ### Local previews
 
 
-1. `npm install`
-1. `npm start`
-1. Go to http://localhost:4000. Your changes in `/website` and `/packages/@uppy` will be watched, your browser will refresh as files change.
+1.  `npm install`
+2.  `npm start`
+3.  Go to http://localhost:4000. Your changes in `/website` and `/packages/@uppy` will be watched, your browser will refresh as files change.
 
 
 Then, to work on, for instance, the XHRUpload example, you would edit the following files:
 Then, to work on, for instance, the XHRUpload example, you would edit the following files:
 
 
@@ -136,7 +185,7 @@ And open <http://localhost:4000/examples/xhrupload/> in your web browser.
 
 
 ## CSS guidelines
 ## CSS guidelines
 
 
-The CSS standards followed in this project closely resemble those from [Medium's CSS Guidelines](https://gist.github.com/fat/a47b882eb5f84293c4ed). If something is not mentioned here, follow their guidelines.
+The CSS standards followed in this project closely resemble those from [Mediums CSS Guidelines](https://gist.github.com/fat/a47b882eb5f84293c4ed). If something is not mentioned here, follow their guidelines.
 
 
 ### Naming conventions
 ### Naming conventions
 
 
@@ -175,7 +224,7 @@ Syntax: `[<namespace>-]<ComponentName>[-descendentName][--modifierName]`
 
 
 ### SASS
 ### SASS
 
 
-This project uses SASS, with some limitations on nesting.  One-level-deep nesting is allowed, but nesting may not extend a selector by using the `&` operator.  For example:
+This project uses SASS, with some limitations on nesting. One-level-deep nesting is allowed, but nesting may not extend a selector by using the `&` operator. For example:
 
 
 ```sass
 ```sass
 /* BAD */
 /* BAD */
@@ -201,11 +250,11 @@ Style to the mobile breakpoint with your selectors, then use `min-width` media q
 
 
 ### Selector, rule ordering
 ### Selector, rule ordering
 
 
-- All selectors are sorted alphabetically and by type.
-- HTML elements go above classes and IDs in a file.
-- Rules are sorted alphabetically.
+*   All selectors are sorted alphabetically and by type.
+*   HTML elements go above classes and IDs in a file.
+*   Rules are sorted alphabetically.
 
 
-```sass
+```scss
 /* BAD */
 /* BAD */
 .wrapper {
 .wrapper {
   width: 940px;
   width: 940px;
@@ -241,28 +290,29 @@ h1 {
 
 
 Before opening a pull request for the new integration, open an issue to discuss said integration with the Uppy team. After discussing the integration, you can get started on it. First off, you need to construct the basic components for your integration. The following components are the current standard:
 Before opening a pull request for the new integration, open an issue to discuss said integration with the Uppy team. After discussing the integration, you can get started on it. First off, you need to construct the basic components for your integration. The following components are the current standard:
 
 
-- `Dashboard`: Inline Dashboard (`inline: true`)
-- `DashboardModal`: Dashboard as a modal
-- `DragDrop`
-- `ProgressBar`
-- `StatusBar`
+*   `Dashboard`: Inline Dashboard (`inline: true`)
+*   `DashboardModal`: Dashboard as a modal
+*   `DragDrop`
+*   `ProgressBar`
+*   `StatusBar`
 
 
-All of these components should function as references to the normal component. Depending on how the framework you're using handles references to the DOM, your approach to creating these may be different. For example, in React, you can assign a property of the component to the reference of a component ([see here](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/packages/%40uppy/react/src/Dashboard.js#L47-L54)). This may differ in your framework, but from what we've found, the concepts are generally pretty similar.
+All these components should function as references to the normal component. Depending on how the framework youre using handles references to the DOM, your approach to creating these may be different. For example, in React, you can assign a property of the component to the reference of a component ([see here](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/packages/%40uppy/react/src/Dashboard.js#L47-L54)). This may differ in your framework, but from what weve found, the concepts are generally pretty similar.
 
 
-If you're familiar with React, Vue or soon Svelte, it might be useful to read through the code of those integrations, as they lay out a pretty good structure. After the basic components have been built, there are a few more important tasks to get done:
+If youre familiar with React, Vue or soon Svelte, it might be useful to read through the code of those integrations, as they lay out a pretty good structure. After the basic components have been built, here are a few more important tasks to get done:
 
 
-- Add TypeScript support in some capacity (if possible)
-- Write documentation
-- Add an example
-- Configuring the build system
+*   Add TypeScript support in some capacity (if possible)
+*   Write documentation
+*   Add an example
+*   Configuring the build system
 
 
 ### Common issues
 ### Common issues
 
 
-Before going into these tasks, there are a few common gotchas that you should be aware of.
+Before going into these tasks, here are a few common gotchas that you should be aware of.
 
 
 #### Dependencies
 #### Dependencies
 
 
 Your `package.json` should resemble something like this:
 Your `package.json` should resemble something like this:
+
 ```json
 ```json
 {
 {
   "name": "@uppy/framework",
   "name": "@uppy/framework",
@@ -287,20 +337,21 @@ The most important part about this is that `@uppy/core` is a peer dependency. If
 
 
 ### Adding TypeScript Support
 ### Adding TypeScript Support
 
 
-This section won't be too in-depth, because TypeScript depends on your framework. As general advice, prefer using `d.ts` files and vanilla JavaScript over TypeScript files. This is of course circumstantial, but it makes handling the build system a lot easier when TypeScript doesn't have to transpiled. The version of typescript in the monorepo is `3.7.5`, so features like `import type` will not work at build time. For upcoming integrations, like Angular, this may be updated.
+This section wont be too in-depth, because TypeScript depends on your framework. As general advice, prefer using `d.ts` files and vanilla JavaScript over TypeScript files. This is circumstantial, but it makes handling the build system a lot easier when TypeScript doesn’t have to transpiled. The version of typescript in the monorepo is `4.1`.
 
 
 ### Writing docs
 ### Writing docs
 
 
 Generally, documentation for integrations can be broken down into a few pieces that apply to every component, and then documentation for each component. The structure should look something like this:
 Generally, documentation for integrations can be broken down into a few pieces that apply to every component, and then documentation for each component. The structure should look something like this:
 
 
-- Installation
-- Initializing Uppy (may vary depending on how the framework handles reactivity)
-- Usage
-- *For each component*
-  - Loading CSS
-  - Props
+*   Installation
+*   Initializing Uppy (may vary depending on how the framework handles reactivity)
+*   Usage
+*   _For each component_
+    *   Loading CSS
+    *   Props
 
 
 It may be easier to copy the documentation of earlier integrations and change the parts that need to be changed rather than writing this from scratch. Preferably, keep the documentation to one page. For the front-matter, write something like:
 It may be easier to copy the documentation of earlier integrations and change the parts that need to be changed rather than writing this from scratch. Preferably, keep the documentation to one page. For the front-matter, write something like:
+
 ```markdown
 ```markdown
 title: Framework Name
 title: Framework Name
 type: docs
 type: docs
@@ -309,17 +360,17 @@ order: 0
 category: "Other Integrations"
 category: "Other Integrations"
 ```
 ```
 
 
-This data is used to generate Uppy's website. Refer to [the section about running the website locally](#website-previews) if you'd like to see how the docs look on the website.
+This data is used to generate Uppy’s website. Refer to [the section about running the website locally](#website-previews) if you’d like to see how the docs look on the website.
 
 
 ### Adding an example
 ### Adding an example
 
 
-This is pretty simple to do, as you can likely use whatever code generation tool for your framework (ex. `create-react-app`) to create this example. Make sure you add the same version of `@uppy/core` to this as your peer dependency required, or you may run into strange issues. Try to include all of the components are some of their functionality. [The React example](https://github.com/transloadit/uppy/blob/master/examples/react-example/App.js) is a great... well example of how to do this well.
+You can likely use whatever code generation tool for your framework (ex. `create-react-app`) to create this example. Make sure you add the same version of `@uppy/core` to this as your peer dependency required, or you may run into strange issues. Try to include all the components are some of their functionality. [The React example](https://github.com/transloadit/uppy/blob/master/examples/react-example/App.js) is a great... well example of how to do this well.
 
 
 ### Integrating the build system
 ### Integrating the build system
 
 
-The biggest part of this is understanding Uppy's build system. The high level description is basically `babel` goes through almost all of the packages and transpiles all the Javascript files in the `src` directory to more compatible JavaScript in the `lib` folder. If you're using vanilla JavaScript for your integration (like React and Vue do), then you can just use this build system and use the files generated as your entry points. 
+The biggest part of this is understanding Uppy’s build system. The high level description is that `babel` goes through almost all the packages and transpiles all the Javascript files in the `src` directory to more compatible JavaScript in the `lib` folder. If youre using vanilla JavaScript for your integration (like React and Vue do), then you can use this build system and use the files generated as your entry points.
 
 
-If you're using some kind of more abstract file format (like Svelte), then you probably want do to a few things: add the directory name to [this `IGNORE` regex](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/bin/build-lib.js#L15); add all of your build dependencies to the root `package.json` (try to keep this small); add a new `build:framework` script to the root `package.json`. This script usually looks something like this:
+If youre using some kind of more abstract file format (like Svelte), then you probably want do to a few things: add the directory name to [this `IGNORE` regex](https://github.com/transloadit/uppy/blob/425f9ecfbc8bc48ce6b734e4fc14fa60d25daa97/bin/build-lib.js#L15); add all your build dependencies to the root `package.json` (try to keep this small); add a new `build:framework` script to the root `package.json`. This script usually looks something like this:
 
 
 ```json
 ```json
 {
 {
@@ -329,4 +380,4 @@ If you're using some kind of more abstract file format (like Svelte), then you p
 }
 }
 ```
 ```
 
 
-Then, add this script to the `build:js` script. Try running the `build:js` script and make sure it does not error. It may also be of use to ensure that global dependencies aren't being used (ex. not having rollup locally and relying on a global install), as these dependencies won't be present on the machine's handling building.
+Then, add this script to the `build:js` script. Try running the `build:js` script and make sure it does not error. It may also be of use to make sure that global dependencies aren’t being used (ex. not having rollup locally and relying on a global install), as these dependencies won’t be present on the machine’s handling building.

+ 1 - 0
website/src/docs/box.md

@@ -63,6 +63,7 @@ You can create a Box App on the [Box Developers site](https://app.box.com/develo
 
 
 Things to note:
 Things to note:
 - Choose "Custom App" and select the "Standard OAuth 2.0 (User Authentication)" app type.
 - Choose "Custom App" and select the "Standard OAuth 2.0 (User Authentication)" app type.
+- Oddly you must enable full write access, or you will get [403 when downloading files](https://support.box.com/hc/en-us/community/posts/360049195613-403-error-while-file-download-API-Call)
 
 
 You'll be redirected to the app page. This page lists the client ID (app key) and client secret (app secret), which you should use to configure Companion as shown above.
 You'll be redirected to the app page. This page lists the client ID (app key) and client secret (app secret), which you should use to configure Companion as shown above.
 
 

+ 6 - 5
website/src/docs/dashboard.md

@@ -234,8 +234,8 @@ An array of UI field objects, or a function that takes a [File Object](https://u
 - `name`, the label shown in the interface.
 - `name`, the label shown in the interface.
 - `placeholder`, the text shown when no value is set in the field. (Not needed when a custom render function is provided)
 - `placeholder`, the text shown when no value is set in the field. (Not needed when a custom render function is provided)
 
 
-Optionally, you can specify `render: ({value, onChange}, h) => void`, a function for rendering a custom form element.
-It gets passed `({value, onChange}, h)` where `value` is the current value of the meta field, `onChange: (newVal) => void` is a function saving the new value and `h` is the `createElement` function from [preact](https://preactjs.com/guide/v10/api-reference#h--createelement).
+Optionally, you can specify `render: ({value, onChange, required}, h) => void`, a function for rendering a custom form element.
+It gets passed `({value, onChange, required}, h)` where `value` is the current value of the meta field, `required` is a boolean that's true if the field `id` is in the `restrictedMetaFields` restriction, and `onChange: (newVal) => void` is a function saving the new value and `h` is the `createElement` function from [preact](https://preactjs.com/guide/v10/api-reference#h--createelement).
 `h` can be useful when using uppy from plain JavaScript, where you cannot write JSX.
 `h` can be useful when using uppy from plain JavaScript, where you cannot write JSX.
 
 
 ```js
 ```js
@@ -248,8 +248,8 @@ uppy.use(Dashboard, {
     {
     {
       id: 'public',
       id: 'public',
       name: 'Public',
       name: 'Public',
-      render ({ value, onChange }, h) {
-        return h('input', { type: 'checkbox', onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'), defaultChecked: value === 'on' })
+      render ({ value, onChange, required }, h) {
+        return h('input', { type: 'checkbox', required, onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'), defaultChecked: value === 'on' })
       },
       },
     },
     },
   ],
   ],
@@ -269,11 +269,12 @@ uppy.use(Dashboard, {
       fields.push({
       fields.push({
         id: 'public',
         id: 'public',
         name: 'Public',
         name: 'Public',
-        render: ({ value, onChange }, h) => {
+        render: ({ value, onChange, required }, h) => {
           return h('input', {
           return h('input', {
             type: 'checkbox',
             type: 'checkbox',
             onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'),
             onChange: (ev) => onChange(ev.target.checked ? 'on' : 'off'),
             defaultChecked: value === 'on',
             defaultChecked: value === 'on',
+            required,
           })
           })
         },
         },
       })
       })

+ 1 - 1
website/src/docs/drop-target.md

@@ -16,7 +16,7 @@ Can be used together with Uppy Dashboard or Drag & Drop plugins, or your custom
 ```js
 ```js
 import DropTarget from '@uppy/drop-target'
 import DropTarget from '@uppy/drop-target'
 
 
-uppy.use(DragDrop, {
+uppy.use(DropTarget, {
   target: document.body,
   target: document.body,
 })
 })
 ```
 ```

+ 5 - 5
website/src/docs/index.md

@@ -19,12 +19,12 @@ Here’s the simplest example html page with Uppy (it uses a CDN bundle, while w
   <head>
   <head>
     <meta charset="utf-8">
     <meta charset="utf-8">
     <title>Uppy</title>
     <title>Uppy</title>
-    <link href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css" rel="stylesheet">
+    <link href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css" rel="stylesheet">
   </head>
   </head>
   <body>
   <body>
     <div id="drag-drop-area"></div>
     <div id="drag-drop-area"></div>
 
 
-    <script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
+    <script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
     <script>
     <script>
       var uppy = Uppy.Core()
       var uppy = Uppy.Core()
         .use(Uppy.Dashboard, {
         .use(Uppy.Dashboard, {
@@ -118,12 +118,12 @@ You can also use a pre-built bundle from Transloadit's CDN: Edgly. `Uppy` will a
 1\. Add a script at the bottom of the closing `</body>` tag:
 1\. Add a script at the bottom of the closing `</body>` tag:
 
 
 ``` html
 ``` html
-<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
 ```
 ```
 
 
 2\. Add CSS to `<head>`:
 2\. Add CSS to `<head>`:
 ``` html
 ``` html
-<link href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css" rel="stylesheet">
+<link href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css" rel="stylesheet">
 ```
 ```
 
 
 3\. Initialize at the bottom of the closing `</body>` tag:
 3\. Initialize at the bottom of the closing `</body>` tag:
@@ -184,5 +184,5 @@ export * from '@uppy/core'
 If you're using Uppy from CDN, those polyfills are already included in the bundle, no need to include anything additionally:
 If you're using Uppy from CDN, those polyfills are already included in the bundle, no need to include anything additionally:
 
 
 ```html
 ```html
-<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.legacy.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
 ```
 ```

+ 2 - 2
website/src/docs/locales.md

@@ -34,8 +34,8 @@ const uppy = new Uppy({
 Add a `<script>` tag with Uppy bundle and the locale pack you’d like to use. You can copy/paste the link from the CDN column in the [locales table](#List-of-locale-packs). The locale will attach itself to the `Uppy.locales` object.
 Add a `<script>` tag with Uppy bundle and the locale pack you’d like to use. You can copy/paste the link from the CDN column in the [locales table](#List-of-locale-packs). The locale will attach itself to the `Uppy.locales` object.
 
 
 ```html
 ```html
-<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
-<script src="https://releases.transloadit.com/uppy/locales/v1.21.0/de_DE.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/locales/v1.22.0/de_DE.min.js"></script>
 
 
 <script>
 <script>
 var uppy = Uppy.Core({
 var uppy = Uppy.Core({

+ 4 - 4
website/src/docs/robodog-form.md

@@ -136,7 +136,7 @@ $(selector).transloadit({
 ```
 ```
 ```html
 ```html
 <!-- The new Robodog way! -->
 <!-- The new Robodog way! -->
-<script src="//releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.js"></script>
+<script src="//releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.js"></script>
 
 
 <script>
 <script>
 window.Robodog.form(selector, {
 window.Robodog.form(selector, {
@@ -148,7 +148,7 @@ window.Robodog.form(selector, {
 Make sure to also include the Uppy css file in your `<head>` tag in case you want to use the `modal: true` option:
 Make sure to also include the Uppy css file in your `<head>` tag in case you want to use the `modal: true` option:
 ```html
 ```html
 <head>
 <head>
-  <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.css">
+  <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.css">
 </head>
 </head>
 ```
 ```
 
 
@@ -160,7 +160,7 @@ Notice how the form is submitted to the inexistant `/uploads` route once all tra
 <html>
 <html>
   <head>
   <head>
     <title>Testing Robodog</title>
     <title>Testing Robodog</title>
-    <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.css">
+    <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.css">
   </head>
   </head>
   <body>
   <body>
     <form id="upload-form" action="/uploads" enctype="multipart/form-data" method="POST">
     <form id="upload-form" action="/uploads" enctype="multipart/form-data" method="POST">
@@ -170,7 +170,7 @@ Notice how the form is submitted to the inexistant `/uploads` route once all tra
       <button type="submit">Upload</button>
       <button type="submit">Upload</button>
     </form>
     </form>
 
 
-    <script src="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.js"></script>
+    <script src="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.js"></script>
     <script type="text/javascript">
     <script type="text/javascript">
     window.Robodog.form('#upload-form', {
     window.Robodog.form('#upload-form', {
       waitForEncoding: true,
       waitForEncoding: true,

+ 2 - 2
website/src/docs/robodog.md

@@ -31,8 +31,8 @@ import '@uppy/robodog/dist/robodog.css'
 If you are not using a bundler, you can also import Robodog using an HTML script tag.
 If you are not using a bundler, you can also import Robodog using an HTML script tag.
 
 
 ```html
 ```html
-<link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.css">
-<script src="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.js"></script>
+<link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.css">
+<script src="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.js"></script>
 <!-- you can now use: window.Robodog.pick() -->
 <!-- you can now use: window.Robodog.pick() -->
 ```
 ```
 
 

+ 2 - 0
website/src/docs/uppy.md

@@ -47,6 +47,7 @@ const uppy = new Uppy({
     maxNumberOfFiles: null,
     maxNumberOfFiles: null,
     minNumberOfFiles: null,
     minNumberOfFiles: null,
     allowedFileTypes: null,
     allowedFileTypes: null,
+    requiredMetaFields: [],
   },
   },
   meta: {},
   meta: {},
   onBeforeFileAdded: (currentFile, files) => currentFile,
   onBeforeFileAdded: (currentFile, files) => currentFile,
@@ -134,6 +135,7 @@ Optionally, provide rules and conditions to limit the type and/or number of file
 - `maxNumberOfFiles` *null | number* — total number of files that can be selected
 - `maxNumberOfFiles` *null | number* — total number of files that can be selected
 - `minNumberOfFiles` *null | number* — minimum number of files that must be selected before the upload
 - `minNumberOfFiles` *null | number* — minimum number of files that must be selected before the upload
 - `allowedFileTypes` *null | array* of wildcards `image/*`, exact mime types `image/jpeg`, or file extensions `.jpg`: `['image/*', '.jpg', '.jpeg', '.png', '.gif']`
 - `allowedFileTypes` *null | array* of wildcards `image/*`, exact mime types `image/jpeg`, or file extensions `.jpg`: `['image/*', '.jpg', '.jpeg', '.png', '.gif']`
+- `requiredMetaFields` *array* of strings
 
 
 `maxNumberOfFiles` also affects the number of files a user is able to select via the system file dialog in UI plugins like `DragDrop`, `FileInput` and `Dashboard`: when set to `1`, they will only be able to select a single file. When `null` or another number is provided, they will be able to select multiple files.
 `maxNumberOfFiles` also affects the number of files a user is able to select via the system file dialog in UI plugins like `DragDrop`, `FileInput` and `Dashboard`: when set to `1`, they will only be able to select a single file. When `null` or another number is provided, they will be able to select multiple files.
 
 

+ 11 - 1
website/src/examples/dashboard/app.es6

@@ -6,6 +6,7 @@ const Instagram = require('@uppy/instagram')
 const Facebook = require('@uppy/facebook')
 const Facebook = require('@uppy/facebook')
 const OneDrive = require('@uppy/onedrive')
 const OneDrive = require('@uppy/onedrive')
 const Zoom = require('@uppy/zoom')
 const Zoom = require('@uppy/zoom')
+const Box = require('@uppy/box')
 const ImageEditor = require('@uppy/image-editor')
 const ImageEditor = require('@uppy/image-editor')
 const Url = require('@uppy/url')
 const Url = require('@uppy/url')
 const Webcam = require('@uppy/webcam')
 const Webcam = require('@uppy/webcam')
@@ -34,6 +35,7 @@ function uppyInit () {
 
 
   const uppy = new Uppy({
   const uppy = new Uppy({
     logger: Uppy.debugLogger,
     logger: Uppy.debugLogger,
+    restrictions: { requiredMetaFields: ['caption'] }
   })
   })
 
 
   uppy.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/', resume: true })
   uppy.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/', resume: true })
@@ -146,6 +148,14 @@ function uppySetOptions () {
     window.uppy.removePlugin(zoomInstance)
     window.uppy.removePlugin(zoomInstance)
   }
   }
 
 
+  const boxInstance = window.uppy.getPlugin('Box')
+  if (opts.Box && !boxInstance) {
+    window.uppy.use(Box, { target: Dashboard, companionUrl: COMPANION })
+  }
+  if (!opts.Box && boxInstance) {
+    window.uppy.removePlugin(boxInstance)
+  }
+
   const webcamInstance = window.uppy.getPlugin('Webcam')
   const webcamInstance = window.uppy.getPlugin('Webcam')
   if (opts.Webcam && !webcamInstance) {
   if (opts.Webcam && !webcamInstance) {
     window.uppy.use(Webcam, {
     window.uppy.use(Webcam, {
@@ -204,7 +214,7 @@ function loadLocaleFromCDN (localeName) {
   const head = document.getElementsByTagName('head')[0]
   const head = document.getElementsByTagName('head')[0]
   const js = document.createElement('script')
   const js = document.createElement('script')
   js.type = 'text/javascript'
   js.type = 'text/javascript'
-  js.src = `https://releases.transloadit.com/uppy/locales/v1.21.0/${localeName}.min.js`
+  js.src = `https://releases.transloadit.com/uppy/locales/v1.22.0/${localeName}.min.js`
 
 
   head.appendChild(js)
   head.appendChild(js)
 }
 }

+ 3 - 0
website/src/examples/dashboard/app.html

@@ -16,6 +16,7 @@
     <li><label for="opts-ScreenCapture"><input type="checkbox" id="opts-ScreenCapture" checked/> Screen Capture</label></li>
     <li><label for="opts-ScreenCapture"><input type="checkbox" id="opts-ScreenCapture" checked/> Screen Capture</label></li>
     <li><label for="opts-GoogleDrive"><input type="checkbox" id="opts-GoogleDrive" checked/> Google Drive</label></li>
     <li><label for="opts-GoogleDrive"><input type="checkbox" id="opts-GoogleDrive" checked/> Google Drive</label></li>
     <li><label for="opts-Dropbox"><input type="checkbox" id="opts-Dropbox" checked/> Dropbox</label></li>
     <li><label for="opts-Dropbox"><input type="checkbox" id="opts-Dropbox" checked/> Dropbox</label></li>
+    <li><label for="opts-Box"><input type="checkbox" id="opts-Box" checked/> Box</label></li>
     <li><label for="opts-Instagram"><input type="checkbox" id="opts-Instagram" checked/> Instagram</label></li>
     <li><label for="opts-Instagram"><input type="checkbox" id="opts-Instagram" checked/> Instagram</label></li>
     <li><label for="opts-Facebook"><input type="checkbox" id="opts-Facebook" checked/> Facebook</label></li>
     <li><label for="opts-Facebook"><input type="checkbox" id="opts-Facebook" checked/> Facebook</label></li>
     <li><label for="opts-OneDrive"><input type="checkbox" id="opts-OneDrive" checked/> OneDrive</label></li>
     <li><label for="opts-OneDrive"><input type="checkbox" id="opts-OneDrive" checked/> OneDrive</label></li>
@@ -46,6 +47,7 @@
     ScreenCapture: document.querySelector('#opts-ScreenCapture'),
     ScreenCapture: document.querySelector('#opts-ScreenCapture'),
     GoogleDrive: document.querySelector('#opts-GoogleDrive'),
     GoogleDrive: document.querySelector('#opts-GoogleDrive'),
     Dropbox: document.querySelector('#opts-Dropbox'),
     Dropbox: document.querySelector('#opts-Dropbox'),
+    Box: document.querySelector('#opts-Box'),
     Instagram: document.querySelector('#opts-Instagram'),
     Instagram: document.querySelector('#opts-Instagram'),
     Facebook: document.querySelector('#opts-Facebook'),
     Facebook: document.querySelector('#opts-Facebook'),
     OneDrive: document.querySelector('#opts-OneDrive'),
     OneDrive: document.querySelector('#opts-OneDrive'),
@@ -67,6 +69,7 @@
     GoogleDrive: true,
     GoogleDrive: true,
     Instagram: true,
     Instagram: true,
     Dropbox: true,
     Dropbox: true,
+    Box: true,
     OneDrive: true,
     OneDrive: true,
     Facebook: false,
     Facebook: false,
     Url: true,
     Url: true,

+ 4 - 1
website/src/examples/dashboard/index.ejs

@@ -26,6 +26,7 @@ const Uppy = require('@uppy/core')
 const Dashboard = require('@uppy/dashboard')
 const Dashboard = require('@uppy/dashboard')
 const GoogleDrive = require('@uppy/google-drive')
 const GoogleDrive = require('@uppy/google-drive')
 const Dropbox = require('@uppy/dropbox')
 const Dropbox = require('@uppy/dropbox')
+const Box = require('@uppy/box')
 const Instagram = require('@uppy/instagram')
 const Instagram = require('@uppy/instagram')
 const Facebook = require('@uppy/facebook')
 const Facebook = require('@uppy/facebook')
 const OneDrive = require('@uppy/onedrive')
 const OneDrive = require('@uppy/onedrive')
@@ -44,7 +45,8 @@ const uppy = new Uppy({
     maxFileSize: 1000000,
     maxFileSize: 1000000,
     maxNumberOfFiles: 3,
     maxNumberOfFiles: 3,
     minNumberOfFiles: 2,
     minNumberOfFiles: 2,
-    allowedFileTypes: ['image/*', 'video/*']
+    allowedFileTypes: ['image/*', 'video/*'],
+    requiredMetaFields: ['caption'],
   }
   }
 })
 })
 .use(Dashboard, {
 .use(Dashboard, {
@@ -63,6 +65,7 @@ const uppy = new Uppy({
 })
 })
 .use(GoogleDrive, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(GoogleDrive, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Dropbox, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Dropbox, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
+.use(Box, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Instagram, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Instagram, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Facebook, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(Facebook, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(OneDrive, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })
 .use(OneDrive, { target: Dashboard, companionUrl: 'https://companion.uppy.io' })

+ 3 - 3
website/src/examples/i18n/app.html

@@ -1,7 +1,7 @@
 <!-- Load Uppy CSS bundle. It is advisable to install Uppy
 <!-- Load Uppy CSS bundle. It is advisable to install Uppy
   from npm/yarn instead, and pick and choose the plugins/styles you need.
   from npm/yarn instead, and pick and choose the plugins/styles you need.
   But for experimenting, you can use Transloadit’s CDN, Edgly: -->
   But for experimenting, you can use Transloadit’s CDN, Edgly: -->
-<link rel="stylesheet" href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css">
+<link rel="stylesheet" href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css">
 
 
 <div class="UppyDragDrop"></div>
 <div class="UppyDragDrop"></div>
 <div class="for-ProgressBar"></div>
 <div class="for-ProgressBar"></div>
@@ -12,8 +12,8 @@
 </div>
 </div>
 
 
 <!-- Load Uppy JS bundle. -->
 <!-- Load Uppy JS bundle. -->
-<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
-<script src="https://releases.transloadit.com/uppy/locales/v1.21.0/ru_RU.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
+<script src="https://releases.transloadit.com/uppy/locales/v1.22.0/ru_RU.min.js"></script>
 <script>
 <script>
   var uppy = Uppy.Core({
   var uppy = Uppy.Core({
     debug: true,
     debug: true,

+ 1 - 1
website/src/examples/markdown-snippets/app.es6

@@ -3,7 +3,7 @@ const marked = require('marked')
 const dragdrop = require('drag-drop')
 const dragdrop = require('drag-drop')
 // Add Robodog JS. It is advisable to install Robodog from npm/yarn.
 // Add Robodog JS. It is advisable to install Robodog from npm/yarn.
 // But for experimenting, you can use also Transloadit’s CDN, Edgly:
 // But for experimenting, you can use also Transloadit’s CDN, Edgly:
-// <script src="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.js"></script>
+// <script src="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.js"></script>
 const robodog = require('@uppy/robodog')
 const robodog = require('@uppy/robodog')
 
 
 const TRANSLOADIT_EXAMPLE_KEY = '35c1aed03f5011e982b6afe82599b6a0'
 const TRANSLOADIT_EXAMPLE_KEY = '35c1aed03f5011e982b6afe82599b6a0'

+ 1 - 1
website/src/examples/markdown-snippets/app.html

@@ -1,6 +1,6 @@
 <!-- Add Robodog styles. It is advisable to install Robodog from npm/yarn.
 <!-- Add Robodog styles. It is advisable to install Robodog from npm/yarn.
   But for experimenting, you can use also Transloadit’s CDN, Edgly:
   But for experimenting, you can use also Transloadit’s CDN, Edgly:
-  <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.10.12/robodog.min.css"> -->
+  <link rel="stylesheet" href="https://releases.transloadit.com/uppy/robodog/v1.11.0/robodog.min.css"> -->
   <link rel="stylesheet" href="/uppy/robodog.min.css">
   <link rel="stylesheet" href="/uppy/robodog.min.css">
   <form id="new" class="form-snippet">
   <form id="new" class="form-snippet">
     <h2>Create a new snippet</h2>
     <h2>Create a new snippet</h2>

+ 3 - 2
website/themes/uppy/layout/index.ejs

@@ -187,8 +187,8 @@
   <p>© <%- date(Date.now(), 'YYYY') %> <a href="https://transloadit.com" target="_blank">Transloadit</a></p>
   <p>© <%- date(Date.now(), 'YYYY') %> <a href="https://transloadit.com" target="_blank">Transloadit</a></p>
 </footer>
 </footer>
 
 
-<link href="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.css" rel="stylesheet">
-<script src="https://releases.transloadit.com/uppy/v1.30.0/uppy.min.js"></script>
+<link href="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.css" rel="stylesheet">
+<script src="https://releases.transloadit.com/uppy/v1.31.0/uppy.min.js"></script>
 
 
 <script>
 <script>
   var TUS_ENDPOINT = 'https://tusd.tusdemo.net/files/'
   var TUS_ENDPOINT = 'https://tusd.tusdemo.net/files/'
@@ -210,6 +210,7 @@
     .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.GoogleDrive, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Instagram, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Instagram, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Dropbox, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Dropbox, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
+    .use(Uppy.Box, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Facebook, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Facebook, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.OneDrive, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.OneDrive, { target: Uppy.Dashboard, companionUrl: COMPANION_ENDPOINT })
     .use(Uppy.Webcam, { target: Uppy.Dashboard })
     .use(Uppy.Webcam, { target: Uppy.Dashboard })