Browse Source

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

Ifedapo Olarewaju 7 years ago
parent
commit
b233d9f669

+ 3 - 0
CHANGELOG.md

@@ -73,6 +73,9 @@ PRs are welcome! Please do open an issue to discuss first if it's a big feature,
 What we need to do to release Uppy 1.0
 
 - [ ] website: big release blog post
+- [ ] chore: hunt down all `@TODO`s and either fix, or remove, or move to github issues/changelog backlog
+- [ ] chore: remove dead code/commented blocks
+- [ ] chore: rewrite all code based on prettier+standardjs.com
 - [ ] ~refactoring: Make `uppy-server` module live in main Uppy repo in `./server` as a second stage todo (after Lerna is done and we're happy) (@ife)
 - [ ] QA: manually test in multiple browsers and mobile devices again (SauceLabs can do Android/iOS too) (@nqst)
 - [ ] QA: add one integration test that uses a Webpack and React/Redux environment (e.g. via `create-react-app`) (@goto-bus-stop)

+ 2 - 2
package.json

@@ -134,8 +134,8 @@
     "build:lib": "babel --version && babel src --source-maps -d lib",
     "build": "npm-run-all --parallel build:js build:css --serial build:gzip size",
     "clean": "rm -rf lib && rm -rf dist",
-    "lint:fix": "eslint src test website/build-examples.js website/update.js website/themes/uppy/source/js/common.js --fix",
-    "lint": "eslint src test website/build-examples.js website/update.js website/themes/uppy/source/js/common.js",
+    "lint:fix": "eslint src test website/scripts website/build-examples.js website/update.js website/themes/uppy/source/js/common.js --fix",
+    "lint": "eslint src test website/scripts website/build-examples.js website/update.js website/themes/uppy/source/js/common.js",
     "lint-staged": "lint-staged",
     "release:major": "env SEMANTIC=major npm run release",
     "release:minor": "env SEMANTIC=minor npm run release",

+ 4 - 1
src/plugins/AwsS3/index.js

@@ -159,7 +159,10 @@ module.exports = class AwsS3 extends Plugin {
         // If no response, we've hopefully done a PUT request to the file
         // in the bucket on its full URL.
         if (!isXml(xhr)) {
-          return { location: xhr.responseURL }
+          // Trim the query string because it's going to be a bunch of presign
+          // parameters for a PUT request—doing a GET request with those will
+          // always result in an error
+          return { location: xhr.responseURL.replace(/\?.*$/, '') }
         }
 
         let getValue = () => ''

+ 3 - 3
src/plugins/Dashboard/index.js

@@ -593,19 +593,19 @@ module.exports = class Dashboard extends Plugin {
 
   uninstall () {
     if (!this.opts.disableInformer) {
-      const informer = this.uppy.getPlugin('Informer')
+      const informer = this.uppy.getPlugin(`${this.id}:Informer`)
       // Checking if this plugin exists, in case it was removed by uppy-core
       // before the Dashboard was.
       if (informer) this.uppy.removePlugin(informer)
     }
 
     if (!this.opts.disableStatusBar) {
-      const statusBar = this.uppy.getPlugin('StatusBar')
+      const statusBar = this.uppy.getPlugin(`${this.id}:StatusBar`)
       if (statusBar) this.uppy.removePlugin(statusBar)
     }
 
     if (!this.opts.disableThumbnailGenerator) {
-      const thumbnail = this.uppy.getPlugin('ThumbnailGenerator')
+      const thumbnail = this.uppy.getPlugin(`${this.id}:ThumbnailGenerator`)
       if (thumbnail) this.uppy.removePlugin(thumbnail)
     }
 

+ 1 - 0
src/scss/_dashboard.scss

@@ -254,6 +254,7 @@
 
 .uppy-DashboardTab-name {
   font-size: 8px;
+  line-height: 11px;
   margin-top: 5px;
   margin-bottom: 0;
   font-weight: 500;

+ 6 - 1
src/server/RequestClient.js

@@ -2,6 +2,11 @@
 
 require('whatwg-fetch')
 
+// Remove the trailing slash so we can always safely append /xyz.
+function stripSlash (url) {
+  return url.replace(/\/$/, '')
+}
+
 module.exports = class RequestClient {
   constructor (uppy, opts) {
     this.uppy = uppy
@@ -12,7 +17,7 @@ module.exports = class RequestClient {
   get hostname () {
     const { uppyServer } = this.uppy.getState()
     const host = this.opts.host
-    return uppyServer && uppyServer[host] ? uppyServer[host] : host
+    return stripSlash(uppyServer && uppyServer[host] ? uppyServer[host] : host)
   }
 
   get defaultHeaders () {

+ 12 - 0
src/server/RequestClient.test.js

@@ -0,0 +1,12 @@
+const RequestClient = require('./RequestClient')
+
+describe('RequestClient', () => {
+  it('has a hostname without trailing slash', () => {
+    const mockCore = { getState: () => ({}) }
+    const a = new RequestClient(mockCore, { host: 'http://server.uppy.io' })
+    const b = new RequestClient(mockCore, { host: 'http://server.uppy.io/' })
+
+    expect(a.hostname).toBe('http://server.uppy.io')
+    expect(b.hostname).toBe('http://server.uppy.io')
+  })
+})

+ 6 - 5
src/views/ProviderView/Browser.js

@@ -21,11 +21,12 @@ const Browser = (props) => {
       <div class="uppy-ProviderBrowser-header">
         <div class={classNames('uppy-ProviderBrowser-headerBar', !props.showBreadcrumbs && 'uppy-ProviderBrowser-headerBar--simple')}>
           <div class="uppy-Provider-breadcrumbsIcon">{props.pluginIcon && props.pluginIcon()}</div>
-          {props.showBreadcrumbs && <Breadcrumbs
-            getFolder={props.getFolder}
-            directories={props.directories}
-            title={props.title} />
-          }
+          {props.showBreadcrumbs && Breadcrumbs({
+            getFolder: props.getFolder,
+            directories: props.directories,
+            title: props.title
+          })}
+          <span class="uppy-ProviderBrowser-user">{props.username}</span>
           <button type="button" onclick={props.logout} class="uppy-ProviderBrowser-userLogout">
             {props.i18n('logOut')}
           </button>

+ 1 - 2
website/_config.yml

@@ -51,9 +51,8 @@ filename_case: 0
 render_drafts: false
 post_asset_folder: false
 highlight:
-  enable: true
+  enable: false
   line_number: false
-  tab_replace:
 
 # Category & Tag
 default_category: uncategorized

+ 51 - 0
website/package-lock.json

@@ -2860,6 +2860,17 @@
         "restore-cursor": "^1.0.1"
       }
     },
+    "clipboard": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.1.tgz",
+      "integrity": "sha512-7yhQBmtN+uYZmfRjjVjKa0dZdWuabzpSKGtyQZN+9C8xlC788SSJjOHWh7tzurfwTqTD5UDYAhIv5fRJg3sHjQ==",
+      "optional": true,
+      "requires": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
     "cliui": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -3508,6 +3519,12 @@
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
     },
+    "delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
+      "optional": true
+    },
     "delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -5737,6 +5754,15 @@
         }
       }
     },
+    "good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+      "optional": true,
+      "requires": {
+        "delegate": "^3.1.2"
+      }
+    },
     "graceful-fs": {
       "version": "4.1.11",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@@ -5878,6 +5904,11 @@
         "sntp": "1.x.x"
       }
     },
+    "he": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+      "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
+    },
     "header-case": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz",
@@ -9708,6 +9739,14 @@
       "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
       "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE="
     },
+    "prismjs": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.14.0.tgz",
+      "integrity": "sha512-sa2s4m60bXs+kU3jcuUwx3ZCrUH7o0kuqnOOINbODqlRrDB7KY8SRx+xR/D7nHLlgfDdG7zXbRO8wJ+su5Ls0A==",
+      "requires": {
+        "clipboard": "^2.0.0"
+      }
+    },
     "private": {
       "version": "0.1.8",
       "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
@@ -10419,6 +10458,12 @@
         }
       }
     },
+    "select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=",
+      "optional": true
+    },
     "semver": {
       "version": "5.4.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
@@ -11232,6 +11277,12 @@
         "process": "~0.11.0"
       }
     },
+    "tiny-emitter": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.2.tgz",
+      "integrity": "sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==",
+      "optional": true
+    },
     "tiny-lr": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.2.1.tgz",

+ 3 - 1
website/package.json

@@ -8,6 +8,7 @@
   "dependencies": {
     "autoprefixer": "^7.2.6",
     "cssnano": "^3.10.0",
+    "he": "^1.1.1",
     "hexo": "^3.7.1",
     "hexo-browsersync": "^0.3.0",
     "hexo-deployer-git": "^0.3.1",
@@ -26,7 +27,8 @@
     "hexo-util": "^0.6.3",
     "js-yaml": "^3.11.0",
     "mkdirp": "0.5.1",
-    "postcss-inline-svg": "^3.1.1"
+    "postcss-inline-svg": "^3.1.1",
+    "prismjs": "^1.14.0"
   },
   "devDependencies": {
     "aliasify": "^2.1.0",

+ 58 - 0
website/scripts/highlight.js

@@ -0,0 +1,58 @@
+/* global hexo */
+const Prism = require('prismjs')
+const entities = require('he')
+const { promisify } = require('util')
+const readFile = promisify(require('fs').readFile)
+const path = require('path')
+
+const unhighlightedCodeRx = /<pre><code class="(.*)?">([\s\S]*?)<\/code><\/pre>/igm
+
+function highlight (lang, code) {
+  const startTag = `<figure class="highlight ${lang}"><table><tr><td class="code"><pre>`
+  const endTag = `</pre></td></tr></table></figure>`
+  let parsedCode = ''
+  if (Prism.languages[lang]) {
+    parsedCode = Prism.highlight(code, Prism.languages[lang])
+  } else {
+    parsedCode = code
+  }
+
+  return startTag + parsedCode + endTag
+}
+
+function prismify (data) {
+  data.content = data.content.replace(unhighlightedCodeRx,
+    (_, lang, code) => highlight(lang, entities.decode(code)))
+
+  return data
+}
+
+function code (args, content) {
+  let lang = ''
+  if (args[0].startsWith('lang:')) {
+    lang = args.shift().replace(/^lang:/, '')
+  }
+
+  return highlight(lang, content)
+}
+
+function includeCode (args) {
+  let lang = ''
+  if (args[0].startsWith('lang:')) {
+    lang = args.shift().replace(/^lang:/, '')
+  }
+
+  const file = path.join(hexo.source_dir, hexo.config.code_dir, args.join(' '))
+  return readFile(file, 'utf8').then((code) => highlight(lang, code.trim()))
+}
+
+// Highlight as many things as we possibly can
+hexo.extend.tag.register('code', code, true)
+hexo.extend.tag.register('codeblock', code, true)
+hexo.extend.tag.register('include_code', includeCode, { async: true })
+hexo.extend.tag.register('include-code', includeCode, { async: true })
+
+// Hexo includes its own code block handling by default which may
+// cause the above to miss some things, so do another pass when the page
+// is done rendering to pick up any code blocks we may have missed.
+hexo.extend.filter.register('after_post_render', prismify)

+ 1 - 1
website/src/api-usage-example.ejs → website/src/api-usage-example.js

@@ -1,7 +1,7 @@
 import Uppy from 'uppy/lib/core'
 import Dashboard from 'uppy/lib/plugins/Dashboard'
 import Tus from 'uppy/lib/plugins/Tus'
- 
+
 Uppy({ autoProceed: false })
   .use(Dashboard, { trigger: '#select-files' })
   .use(Tus, { endpoint: 'https://master.tus.io/files/' })

+ 0 - 149
website/src/docs/architecture.md

@@ -1,149 +0,0 @@
----
-type: api
-order: 10
-title: "Architecture"
-permalink: api/architecture/
-published: false
----
-
-Uppy file uploader consists of a lean [Core](https://github.com/transloadit/uppy/blob/master/src/core/Core.js) module and [Plugins](https://github.com/transloadit/uppy/tree/master/src/plugins) (see simple [Input](https://github.com/transloadit/uppy/blob/master/src/plugins/Formtag.js) as an example) that extend it’s functionality. Like this:
-
-{% include_code lang:js ../api-usage-example.ejs %}
-
-## Core
-
-1. Core module orchestrates Uppy plugins, stores `state` with `files`, and exposes useful methods like `addFile`, `setState`, `upload` to the user and plugins. Plugins are added to Uppy with `.use(Plugin, opts)` API, like so: `.use(DragDrop, {target: 'body'})`.
-
-2. Internally Core then instantiates plugins via `new Plugin(this, opts)`, passing options to them, then places them in `plugins` object, nested by type: `uploader`, `progressindicator`, `acquirer`, etc. Core then iterates over `plugins` and calls `install` on each of them. In it’s `install` method a plugin can set event listeners to react to things happening in Uppy (upload progress, file was removed), or do anything else needed on init.
-
-3. Each time `state` is updated with `setState(statePatch)`, Core runs `updateAll()` method that re-renders all of the view plugins (components) that have been mounted in the DOM somewhere, passing the new state to each of them.
-
-Core is very lean (at least we try to keep it sobe), and acts as a glue that ties together all the plugins, shared data and functionality together. It keeps `state` with `files`, `capabilities`, plus exposes a few methods that are called by plugins to update state — adding files, adding preview thumbnails to images, updating progress for each file and total progress, etc.
-
-*Comment: There is a discussion that these methods could in theory all live in Utils or be standalone modules used by plugins and the user directly, see https://github.com/transloadit/uppy/issues/116#issuecomment-247695921.*
-
-### getState()
-
-Returns current state.
-
-### setState({itemToUpdate: data})
-
-Updates state with new state (Object.assign({}, this.state, newState)) and then runs `updateAll()`.
-
-### updateAll()
-
-Iterates over all `plugins` and runs `update()` on each of them.
-
-### updateMeta(data, fileID)
-
-Given `{ size: 1200 }` adds that metadata to a file.
-
-### addFile(file)
-
-Adds a new file to `state`. This method is used by `acquirer` plugins like Drag & Drop, Webcam and Google Drive,
-or can be called by the user on uppy instance directly: `uppy.addFile(myFile)`.
-
-Normalizes that file too: tries to figure out file type by extension if mime is missing, use noname if name is missing, sets progress: 0  and so on.
-
-### removeFile(fileID)
-
-Removes file from `state.files`.
-
-### addThumbnail(fileID)
-
-Reads image from file’s data object in base64 and resizes that, using canvas. Then `state` is updated with a file that has thumbnail in it. Thumbnails are used for file previews by plugins like Dashboard.
-
-### state.capabilities
-
-Core (or plugins) check and set capabilities, like: `resumable: true` (this is set by Tus Plugin), `touch: false`, `dragdrop: true`, that could possibly be used by all plugins.
-
-### log(msg)
-
-Logs stuff to console only if user specified `debug: true`, silent in production.
-
-### core.on('event', action), core.emit('event')
-
-An event emitter embedded into Core that is passed to Plugins, and can be used directly on the instance. Used by Plugins for communicating with other Plugins and Core.
-
-## Events
-
-- Core listens for `core:upload-progress` event and calculates speed & ETA for all files currently in progress. *Uploader plugins could just call core.updateProgress().*
-- Core checks if browser in offline or online and emits `core:is-offline` or `core:is-online` that other plugins can listen to.
-- Any plugin can emit `informer` event that `Informer` plugin can use to display info bubbles. Currently only used in the Dashboard plugin. Example: `core.emitter.emit('informer', 'Connected!', 'success', 3000)`. *(Should this be a Core method instead of Plugin? Could be replaced by core.inform(info) method that will just update state with info)*
-- Uploader plugins listen to `core:upload` event (can be emitted after a file has been added or when upload button has been pressed), get files via `core.getState().files`, filter those that are not marked as complete and upload them, emitting progress events.
-
-*See discussion about Core and Event Emitter: https://github.com/transloadit/uppy/issues/116#issuecomment-247695921*
-
-## Plugins
-
-Plugins extend the functionality of Core (which itself is very much barebones, does almost nothing). Plugins actually do the work — select files, modify and upload them, and show the UI.
-
-Plugins that have some UI can be mounted anywhere in the dom. With current design you can have a Drag & Drop area in `#dragdrop` and Progressbar in `body`. Plugins can also be mounted into other plugins that support that, like Dashboard.
-
-Each plugin extends `Plugin` class with default methods that can be overridden:
-
-### update()
-
-Gets called when state changes and `updateAll()` is called from Core. Checks if a DOM element (tree) has been created with a reference for it stored in plugin’s `this.el`. If so, creates a new element (tree) `const newEl = this.render(currentState)` for current plugin and then calls `yo.update(this.el, newEl)` to effectively update the existing element to match the new one (morphdom is behind that).
-
-All together now:
-
-``` javascript
-const newEl = this.render(currentState)
-yo.update(this.el, newEl)
-```
-
-### mount(target, plugin)
-
-Called by the plugin itself, if it is a UI plugin that needs to do something in DOM. A `target` is passed as an argument. There are two possible scenarios for mounting:
-
-1\. If `typeof target === 'string'`, then the element (tree) is rendered and gets mounted to the DOM:
-
-``` javascript
-this.el = plugin.render(this.core.state)
-document.querySelector(target).appendChild(this.el)
-```
-
-2\. If `typeof target === 'object'`, then that plugin is found in `plugins` object of Core and `addTarget()` is called on that plugin.
-
-``` javascript
-const targetPlugin = this.core.getPlugin(targetPluginName)
-const selectorTarget = targetPlugin.addTarget(plugin)
-```
-
-### focus()
-
-Called when plugin is in focus, like when that plugin’s tab is selected in the Dashboard.
-
-### install()
-
-Called by Core when it instantiates new plugins. In `install`
-a plugin can extend global state with its own state (like `{ modal: { isHidden: true } }`), or do anything needed on initialization, including `mount()`.
-
-### Plugins are currently of the following types:
-
-### acquirer
-
-- **DragDrop** — simple DragDrop, once file is dropped on a target, it gets added to state.files. “click here to select” too
-- **GoogleDrive** — GoogleDrive UI, uses uppy-server component. Files are downloaded from Google Drive to uppy-server, without having to go through the client, saving bandwidth and space
-- **Formtag** — simple input[type=file] element
-- **Webcam** — allows to take pictures with your webcam, works in most browsers, except Safari. Flash (ugh) is used for fallback
-
-### orchestrator
-
-*Should probably be called UI*
-
-- **Dashboard** — the full-featured interface for interacting with Uppy. Supports drag & dropping files, provides UI for Google Drive, Webcam and any other plugin, shows selected files, shows progress on them.
-
-### progressindicator
-
-- **ProgressBar** — tiny progressbar that can be mounted anywhere.
-
-### uploader
-
-- **Tus** — tus resumable file uploads, see http://tus.io
-- **Multipart** — regular form/multipart/xhr uploads
-
-### modifier
-
-- **Metadata** — allows adding custom metadata fields, data from each field is then added to a file

+ 19 - 18
website/src/docs/contributing.md

@@ -4,7 +4,7 @@ type: docs
 order: 4
 ---
 
-## Uppy Development
+## Uppy development
 
 Fork the repository into your own account first. See the [GitHub Help](https://help.github.com/articles/fork-a-repo/) article for instructions.
 
@@ -16,7 +16,7 @@ cd uppy
 npm install
 ```
 
-Our website’s examples section is also our playground, please read [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.
 
 ## Tests
 
@@ -24,7 +24,7 @@ Unit tests are using Jest and can be run with:
 
 `npm run test:unit`
 
-For acceptance (or end to end) tests, we use [Webdriverio](http://webdriver.io). For it to run locally, you need to install selenium standalone server, just follow [the guide](http://webdriver.io/guide.html) to do so. You can also install Selenium Standalone server from NPM:
+For acceptance (or 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:
 
 ```bash
 npm install selenium-standalone -g
@@ -35,30 +35,31 @@ And then launch it:
 
 `selenium-standalone start`
 
-After youve installed and launched the selenium standalone server, run:
+After you have installed and launched the selenium standalone server, run:
 
 `npm run test:acceptance:local`
 
 These tests are also run automatically on Travis builds with [SauceLabs](https://saucelabs.com/) cloud service using different OSes.
 
-## Website Development
+## Website development
 
-We keep the [uppy.io](http://uppy.io) website in `./website` so it’s easy to keep docs & code in sync as we’re still iterating at high velocity.
+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.
 
 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!
 
-Even though bundled in this repo, the website is regarded as a separate project. So 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
 
-It's recommended to exclude `./website/public/` from your editor if you want efficient searches.
+It is recommended to exclude `./website/public/` from your editor if you want efficient searches.
 
 To install the required node modules, type:
+
 ```bash
 npm install && cd website && npm install && cd ..
 ```
 
-For local previews on http://127.0.0.1:4000 type:
+For local previews on http://127.0.0.1:4000, type:
 
 ```bash
 npm start
@@ -66,7 +67,7 @@ npm start
 
 This will watch the website, as well as Uppy, as the examples, and rebuild everything and refresh your browser as files change.
 
-Then, to work on e.g. the XHRUpload example, you'd edit the following files:
+Then, to work on, for instance, the XHRUpload example, you would edit the following files:
 
 ```bash
 atom src/core/Core.js \
@@ -75,13 +76,13 @@ atom src/core/Core.js \
   website/src/examples/xhrupload/app.es6
 ```
 
-And open <http://0.0.0.0:4000/examples/xhrupload/> in your webbrowser.
+And open <http://0.0.0.0: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 it's not mentioned here, follow their 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.
 
-### Naming Conventions
+### Naming conventions
 
 This project uses naming conventions adopted from the SUIT CSS framework.
 [Read about them here](https://github.com/suitcss/suit/blob/master/doc/naming-conventions.md).
@@ -118,7 +119,7 @@ Syntax: [<namespace>-]<ComponentName>[-descendentName][--modifierName]
 
 ### 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
 /* BAD */
@@ -138,11 +139,11 @@ This project uses SASS, with some limitations on nesting.  One-level deep nestin
 }
 ```
 
-### Mobile-first Responsive Approach
+### Mobile-first responsive approach
 
 Style to the mobile breakpoint with your selectors, then use `min-width` media queries to add any styles to the tablet or desktop breakpoints.
 
-### Selector, Rule Ordering
+### Selector, rule ordering
 
 - All selectors are sorted alphabetically and by type.
 - HTML elements go above classes and IDs in a file.

+ 7 - 7
website/src/docs/plugins.md

@@ -8,8 +8,8 @@ order: 10
 Plugins are what makes Uppy useful: they help select, manipulate and upload files.
 
 - **Acquirers (various ways of picking files):**
-  - [Dashboard](/docs/dashboard) — full featured sleek UI with file previews, metadata editing, upload/pause/resume/cancel buttons and more. Includes `StatusBar` and `Informer` plugins by default
-  - [DragDrop](/docs/dragdrop) — plain and simple drag and drop area
+  - [Dashboard](/docs/dashboard) — full-featured sleek UI with file previews, metadata editing, upload/pause/resume/cancel buttons and more. Includes `StatusBar` and `Informer` plugins by default
+  - [DragDrop](/docs/dragdrop) — plain and simple drag-and-drop area
   - [FileInput](/docs/fileinput) — even more plain and simple, just a button
   - [Webcam](/docs/webcam) — upload selfies or audio / video recordings
   - [Provider Plugins](/docs/providers) (remote sources that work through [Uppy Server](/docs/uppy-server/))
@@ -27,13 +27,13 @@ Plugins are what makes Uppy useful: they help select, manipulate and upload file
   - [Informer](/docs/informer) — show notifications
 - **Helpers:**
   - [GoldenRetriever](/docs/golden-retriever) — restore files and continue uploading after a page refresh or a browser crash
-  - [Form](/docs/form) — collect metadata from `<form>` right before Uppy upload, then optionally append results back to the form
+  - [Form](/docs/form) — collect metadata from `<form>` right before the Uppy upload, then optionally append results back to the form
 - **Encoding Services:**
   - [Transloadit](/docs/transloadit) — manipulate and transcode uploaded files using the [transloadit.com](https://transloadit.com) service
 
-## Common Options
+## Common options
 
-Each plugin can have any number of options (please see specific plugin for details), but these are shared between some:
+Each plugin can have any number of options (please see specific plugins for details), but these are shared between some:
 
 ### `id`
 
@@ -64,11 +64,11 @@ uppy.use(Dashboard, {
 uppy.use(GoogleDrive, {target: Dashboard})
 ```
 
-In the example above the `Dashboard` gets rendered into an element with ID `uppy`, while `GoogleDrive` is rendered into the `Dashboard` itself.
+In the example above, the `Dashboard` gets rendered into an element with ID `uppy`, while `GoogleDrive` is rendered into the `Dashboard` itself.
 
 ### `locale: {}`
 
-Same as with Uppy.Core’s setting from above, this allows you to override plugin’s locale string, so that instead of `Select files` in English, your users will see `Выберите файлы` in Russian. Example:
+Same as with Uppy.Core’s setting above, this allows you to override plugin’s locale string, so that instead of `Select files` in English, your users will see `Выберите файлы` in Russian. Example:
 
 ```js
 .use(FileInput, {

+ 47 - 47
website/src/docs/server.md

@@ -5,23 +5,23 @@ permalink: docs/server/
 order: 2
 ---
 
-Drag and Drop, Webcam, basic file manipulation (adding metadata, for example) and uploading via tus resumable uploads or XHR/Multipart are all possible using just the uppy client module.
+Drag and drop, webcam, basic file manipulation (adding metadata, for example) and uploading via tus-resumable uploads or XHR/Multipart are all possible using just the Uppy client module.
 
-However, if you add [Uppy Server](https://github.com/transloadit/uppy-server) to the mix, your users will be able to select files from remote sources, such as Instagram, Google Drive and Dropbox, bypassing the client (so a 5 GB video isn’t eating into your users’ data plans), and then uploaded to the final destination. Files are removed from Uppy Server after an upload is complete, or after a reasonable timeout. Access tokens also don’t stick around for long, for security.
+However, if you add [Uppy Server](https://github.com/transloadit/uppy-server) to the mix, your users will be able to select files from remote sources, such as Instagram, Google Drive and Dropbox, bypassing the client (so a 5 GB video isn’t eating into your users’ data plans), and then uploaded to the final destination. Files are removed from Uppy Server after an upload is complete, or after a reasonable timeout. Access tokens also don’t stick around for long, for security reasons.
 
 Uppy Server handles the server-to-server communication between your server and file storage providers such as Google Drive, Dropbox, Instagram, etc.
 
-## Supported Providers
+## Supported providers
 
-As of now Uppy Server is integrated to work with:
+As of now, Uppy Server is integrated to work with:
 
 - Google Drive
 - Dropbox
 - Instagram
-- Remote Urls
+- Remote URLs
 - Amazon S3
 
-## Install
+## Installation
 
 ```bash
 npm install uppy-server
@@ -29,11 +29,11 @@ npm install uppy-server
 
 ## Usage
 
-Uppy Server may either be used as a pluggable express app, which you plug to your already existing server, or it may simply be run as a standalone server:
+Uppy Server may either be used as a pluggable express app, which you plug into your already existing server, or it may simply be run as a standalone server:
 
-### Plug to already existing server
+### Plugging into an already existing server
 
-To plug Uppy Server to an existing server, simply call on its `.app` method, passing in an [options](#Options) object as parameter.
+To plug Uppy Server into an existing server, simply call on its `.app` method, passing in an [options](#Options) object as a parameter.
 
 ```javascript
 
@@ -66,7 +66,7 @@ app.use(uppy.app(options))
 ```
 See [Options](#Options) for valid configuration options.
 
-To enable Uppy Socket for realtime upload progress, you can call the `socket` method like so:
+To enable Uppy Socket for realtime upload progress, you can call the `socket` method, like so:
 
 ```javascript
 ...
@@ -75,9 +75,9 @@ var server = app.listen(PORT)
 uppy.socket(server, options)
 
 ```
-This takes your `server` instance and your Uppy [options](#Options) as parameters.
+This takes your `server` instance and your Uppy [Options](#Options) as parameters.
 
-### Run as standalone server
+### Running as a standalone server
 
 > Please ensure that the required environment variables are set before running/using Uppy Server as a standalone server. See [Configure Standalone](#Configure-Standalone) for the variables required.
 
@@ -101,7 +101,7 @@ If you cloned the repo from GitHub and want to run it as a standalone server, yo
 npm start
 ```
 
-You can also pass in the path to your json config file like so:
+You can also pass in the path to your JSON config file, like so:
 
 ```bash
 uppy-server --config /path/to/uppyconf.json
@@ -113,11 +113,11 @@ or
 npm start -- --config /path/to/uppyconf.json
 ```
 
-Please see [options](#Options) for possible options.
+Please see [Options](#Options) for possible options.
 
-#### Configure Standalone
+#### Configuring a standalone server
 
-To run Uppy Server as a standalone server, you are required to set your Uppy [options](#Options) via environment variables:
+To run Uppy Server as a standalone server, you are required to set your Uppy [Options](#Options) via environment variables:
 
 ```bash
 ####### Mandatory variables ###########
@@ -131,24 +131,24 @@ export UPPYSERVER_DATADIR="PATH/TO/DOWNLOAD/DIRECTORY"
 
 ###### Optional variables ##########
 
-# corresponds to the server.protocol option. defaults to http
+# corresponds to the server.protocol option, defaults to http
 export UPPYSERVER_PROTOCOL="YOUR SERVER PROTOCOL"
-# the port to start the server on. defaults to 3020
+# the port on which to start the server, defaults to 3020
 export UPPYSERVER_PORT="YOUR SERVER PORT"
-# corresponds to the server.port option. defaults to ''
+# corresponds to the server.port option, defaults to ''
 export UPPYSERVER_PATH="/SERVER/PATH/TO/WHERE/UPPY-SERVER/LIVES"
 
 # use this in place of UPPYSERVER_PATH if the server path should not be
-# handled by the express.js app but maybe by an external server configuration
+# handled by the express.js app, but maybe by an external server configuration
 # instead (e.g Nginx).
 export UPPYSERVER_IMPLICIT_PATH="/SERVER/PATH/TO/WHERE/UPPY/SERVER/LIVES"
 
-# comma separated client hosts to whitlelist by the server
+# comma-separated client hosts to whitlelist by the server
 # if not specified, the server would allow any host
 export UPPY_ENDPOINTS="localhost:3452,uppy.io"
 
 # corresponds to the redisUrl option
-# This also enables redis session storage if set
+# this also enables Redis session storage if set
 export UPPYSERVER_REDIS_URL="REDIS URL"
 
 # to enable Dropbox
@@ -163,7 +163,7 @@ export UPPYSERVER_GOOGLE_SECRET="YOUR GOOGLE SECRET"
 export UPPYSERVER_INSTAGRAM_KEY="YOUR INSTAGRAM KEY"
 export UPPYSERVER_INSTAGRAM_SECRET="YOUR INSTAGRAM SECRET"
 
-# to enable s3
+# to enable S3
 export UPPYSERVER_AWS_KEY="YOUR AWS KEY"
 export UPPYSERVER_AWS_SECRET="YOUR AWS SECRET"
 export UPPYSERVER_AWS_BUCKET="YOUR AWS S3 BUCKET"
@@ -177,7 +177,7 @@ export UPPYSERVER_DOMAINS="sub1.domain.com,sub2.domain.com,sub3.domain.com"
 # corresponds to the sendSelfEndpoint option
 export UPPYSERVER_SELF_ENDPOINT="THIS SHOULD BE SAME AS YOUR DOMAIN + PATH"
 
-# comma separated urls
+# comma-separated URLs
 # corresponds to the uploadUrls option
 export UPPYSERVER_UPLOAD_URLS="http://master.tus.io/files/,https://master.tus.io/files/"
 ```
@@ -221,37 +221,37 @@ See [env.example.sh](https://github.com/transloadit/uppy-server/blob/master/env.
 }
 ```
 
-1. **filePath(required)** - Full path to the directory where provider files would temporarily be downloaded to.
+1. **filePath(required)** - Full path to the directory to which provider files would be downloaded temporarily.
 
-2. **redisUrl(optional)** - URL to running redis server. If this is set, the state of uploads would be stored temporarily. This helps for resumed uploads after a browser crash from the client. The stored upload would be sent back to the client on reconnection.
+2. **redisUrl(optional)** - URL to running Redis server. If this is set, the state of uploads would be stored temporarily. This helps for resumed uploads after a browser crash from the client. The stored upload would be sent back to the client on reconnection.
 
-3. **providerOptions(optional)** - An object containing credentials (`key` and `secret`) for each provider you would like to enable. Please see [here for the list of supported providers](#Supported-Providers).
+3. **providerOptions(optional)** - An object containing credentials (`key` and `secret`) for each provider you would like to enable. Please see [the list of supported providers](#Supported-Providers).
 
-4. **server(optional)** - An object with details mainly used to carry out oauth authentication from any of the enabled providers above. Though it is optional, it is required if you would be enabling any of the supported providers. The following are the server options you may set:
+4. **server(optional)** - An object with details, mainly used to carry out oauth authentication from any of the enabled providers above. Though it is optional, it is required if you would be enabling any of the supported providers. The following are the server options you may set:
 
   - protocol - `http | https`
   - host(required) - your server host (e.g localhost:3020, mydomain.com)
-  - path - the server path to where the uppy app is sitting (e.g if uppy server is at `mydomain.com/uppy`, then the path would be `/uppy`).
-  - oauthDomain - if you have multiple instances of uppy server with different (and maybe dynamic) subdomains, you can set a master domain (e.g `sub1.mydomain.com`) to handle your oauth authentication for you. This would then redirect to the slave subdomain with the required credentials on completion.
-  - validHosts - if you are setting a master `oauthDomain`, you need to set a list of valid hosts, so the master oauth handler can validate the host of the uppy instance requesting the authentication. This is basically a list of valid domains running your uppy server instances. The list may also contain regex patterns. e.g `['sub2.mydomain.com', 'sub3.mydomain.com', '(\\w+).mydomain.com']`
+  - path - the server path to where the Uppy app is sitting (e.g if Uppy Server is at `mydomain.com/uppy`, then the path would be `/uppy`).
+  - oauthDomain - if you have multiple instances of Uppy Server with different (and perhaps dynamic) subdomains, you can set a master domain (e.g `sub1.mydomain.com`) to handle your oauth authentication for you. This would then redirect to the slave subdomain with the required credentials on completion.
+  - validHosts - if you are setting a master `oauthDomain`, you need to set a list of valid hosts, so the master oauth handler can validate the host of the Uppy instance requesting the authentication. This is basically a list of valid domains running your Uppy Server instances. The list may also contain regex patterns. e.g `['sub2.mydomain.com', 'sub3.mydomain.com', '(\\w+).mydomain.com']`
 
 5. **sendSelfEndpoint(optional)** - This is basically the same as the `server.host + server.path` attributes. The major reason for this attribute is that, when set, it adds the value as the `i-am` header of every request response.
 
 6. **customProviders(optional)** - This option enables you to add custom providers along with the already supported providers. See [Adding Custom Providers](#Adding-Custom-Providers) for more information.
 
-7. **uploadUrls(optional)** - An array of urls (full paths). If specified, Uppy Server will only accept uploads to these urls (useful when you want to make sure an Uppy Server instance is only allowed to upload to your servers, for example).
+7. **uploadUrls(optional)** - An array of URLs (full paths). If specified, Uppy Server will only accept uploads to these URLs (useful when you want to make sure an Uppy Server instance is only allowed to upload to your servers, for example).
 
 8. **secret(required)** - A secret string which Uppy Server uses to generate authorization tokens.
 
 9. **debug(optional)** - A boolean flag to tell Uppy Server whether or not to log useful debug information while running.
 
-### S3 Options
+### S3 options
 
 The S3 uploader has some options in addition to the ones necessary for authentication.
 
 #### `s3.getKey(req, filename)`
 
-Get the key name for a file. The key is the file path that the file will be uploaded to in your bucket. This option should be a function receiving two arguments: `req`, the HTTP request, and the original `filename` of the uploaded file. It should return a string `key`. The `req` parameter can be used to upload to a user-specific folder in your bucket, for example:
+Get the key name for a file. The key is the file path to which the file will be uploaded in your bucket. This option should be a function receiving two arguments: `req`, the HTTP request, and the original `filename` of the uploaded file. It should return a string `key`. The `req` parameter can be used to upload to a user-specific folder in your bucket, for example:
 
 ```js
 app.use(authenticationMiddleware)
@@ -265,13 +265,13 @@ app.use(uppy.app({
 
 The default value simply returns `filename`, so all files will be uploaded to the root of the bucket as their original file name.
 
-### Run in Kubernetes
+### Running in Kubernetes
 
 We have [a detailed guide on running Uppy Server in Kubernetes](https://github.com/transloadit/uppy-server/blob/master/KUBERNETES.md) for you, that’s how we currently run our example server at http://server.uppy.io.
 
-### Adding Custom Providers
+### Adding custom providers
 
-As of now, Uppy Server supports **Google Drive**, **Dropbox**, **Instagram**, and **Url** (remote urls) out of the box, but you may also choose to add your custom providers. You can do this by passing the `customProviders` option when calling the uppy `app` method. The custom provider is expected to support Oauth 1 or 2 for authentication/authorization.
+As of now, Uppy Server supports **Google Drive**, **Dropbox**, **Instagram**, and **URL** (remote urls) out of the box, but you may also choose to add your own custom providers. You can do this by passing the `customProviders` option when calling the Uppy `app` method. The custom provider is expected to support Oauth 1 or 2 for authentication/authorization.
 
 ```javascript
 let options = {
@@ -293,26 +293,26 @@ let options = {
 uppy.app(options)
 ```
 
-The `customProviders` option should be an object containing each custom provider. Each custom provider would in turn be an object with two keys, `config` and `module`. The `config` option would contain Oauth API settings, while the `module` would point to the provider module.
+The `customProviders` option should be an object containing each custom provider. Each custom provider would, in turn, be an object with two keys, `config` and `module`. The `config` option would contain Oauth API settings, while the `module` would point to the provider module.
 
 To work well with Uppy Server, the **Module** must be a class with the following methods.
 
-1. `list (options, done)` - lists json data of user files (e.g list of all the files in a particular directory).
+1. `list (options, done)` - lists JSON data of user files (e.g. list of all the files in a particular directory).
   - `options` - is an object containing the following attributes
-    - token - authorization token(retrieved from oauth process) to send along with your request
-    - directory - the `id/name` of the directory whose data is to be retrieved. This may be ignored if it doesn't apply to your provider
-    - query - expressjs query params object received by the server(just in case there's some data you need in there).
-  - `done (err, response, body)` - the callback that should be called when the request to your provider is done. As the signature indicates the following data should be passed along to the callback `err`, `response`, and `body`.
+    - token - authorization token (retrieved from oauth process) to send along with your request
+    - directory - the `id/name` of the directory from which data is to be retrieved. This may be ignored if it doesn't apply to your provider
+    - query - expressjs query params object received by the server (just in case there is some data you need in there).
+  - `done (err, response, body)` - the callback that should be called when the request to your provider is made. As the signature indicates, the following data should be passed along to the callback `err`, `response`, and `body`.
 2. `download (options, onData, onResponse)` - downloads a particular file from the provider.
-  - `options` - is an object containing the following attributes
-    - token - authorization token(retrieved from oauth process) to send along with your request.
-    - id - id of the file being downloaded.
+  - `options` - is an object containing the following attributes:
+    - token - authorization token (retrieved from oauth process) to send along with your request.
+    - id - ID of the file being downloaded.
   - `onData (chunk)` - a callback that should be called with each data chunk received on download. This is useful if the size of the downloaded file can be pre-determined. This would allow for pipelined upload of the file (to the desired destination), while the download is still going on.
   - `onResponse (response)` - if the size of the downloaded file can not be pre-determined by Uppy Server, then this callback should be called in place of the `onData` callback. This callback would be called after the download is done, and would take the downloaded data (response) as the argument.
 
 ## Development
 
-1\. To setup Uppy Server for local development, please clone the repo and install like so:
+1\. To set up Uppy Server for local development, please clone the repo and install, like so:
 
 ```bash
 git clone https://github.com/transloadit/uppy-server && cd uppy-server && npm install

+ 3 - 3
website/src/docs/stores.md

@@ -5,7 +5,7 @@ title: "Custom Stores"
 permalink: docs/stores/
 ---
 
-> This section is about storing internal application state, if you work with React/Redux, for example. If none of this rings a bell, you can safely skip this section.
+> This section concerns storing the internal application state, if you work with React/Redux, for example. If none of this rings a bell, you can safely skip this section.
 
 By default, Uppy stores its internal state in an object.
 
@@ -16,11 +16,11 @@ Uppy comes with two state management solutions (stores):
  - `DefaultStore`, a simple object-based store.
  - `ReduxStore`, a store that uses a key in a Redux store.
 
-There are also some third party stores:
+There are also some third-party stores:
 
  - [uppy-store-ngrx](https://github.com/rimlin/uppy-store-ngrx/), keeping Uppy state in a key in an [Ngrx](https://github.com/ngrx/platform) store for use with Angular.
 
-## Using Stores
+## Using stores
 
 To use a store, pass an instance to the [`store` option](/docs/uppy#store-defaultstore) in the Uppy constructor:
 

+ 1 - 1
website/src/docs/uppy.md

@@ -365,7 +365,7 @@ Update the state for a single file. This is mostly useful for plugins that may w
 
 ### `uppy.setMeta(data)`
 
-Alters global `meta` object is state, the one that can be set in Uppy options and gets merged with all newly added files. Calling `setMeta` will also merge newly added meta data with previously selected files.
+Alters global `meta` object in state, the one that can be set in Uppy options and gets merged with all newly added files. Calling `setMeta` will also merge newly added meta data with previously selected files.
 
 ```js
 uppy.setMeta({ resize: 1500, token: 'ab5kjfg' })

+ 15 - 16
website/src/docs/writing-plugins.md

@@ -5,16 +5,15 @@ permalink: docs/writing-plugins/
 order: 11
 ---
 
-There are a few useful Uppy plugins out there, but there might come a time when you’ll want to build your own.
-Plugins can hook into the upload process or render a custom UI, typically to:
+There are already a few useful Uppy plugins out there, but there might come a time when you will want to build your own. Plugins can hook into the upload process or render a custom UI, typically to:
 
- - Render some custom UI element, e.g. [StatusBar](/docs/statusbar) or [Dashboard](/docs/dashboard).
- - Do the actual uploading, e.g. [XHRUpload](/docs/xhrupload) or [Tus](/docs/tus).
- - Interact with a third party service to process uploads correctly, e.g. [Transloadit](/docs/transloadit) or [AwsS3](/docs/aws-s3).
+ - Render some custom UI element, e.g., [StatusBar](/docs/statusbar) or [Dashboard](/docs/dashboard).
+ - Do the actual uploading, e.g., [XHRUpload](/docs/xhrupload) or [Tus](/docs/tus).
+ - Interact with a third-party service to process uploads correctly, e.g., [Transloadit](/docs/transloadit) or [AwsS3](/docs/aws-s3).
 
 ## Creating A Plugin
 
-Plugins are classes that extend from Uppy's `Plugin` class. Each plugin has an `id` and a `type`. `id`s are used to uniquely identify plugins. A `type` can be anything—some plugins use `type`s to determine whether to do something to some other plugin. For example, when targeting plugins at the builtin `Dashboard` plugin, the Dashboard uses the `type` to figure out where to mount different UI elements. `'acquirer'` type plugins are mounted into the tab bar, while `'progressindicator'` type plugins are mounted into the progress bar area.
+Plugins are classes that extend from Uppy's `Plugin` class. Each plugin has an `id` and a `type`. `id`s are used to uniquely identify plugins. A `type` can be anything—some plugins use `type`s to determine whether to do something to some other plugin. For example, when targeting plugins at the built-in `Dashboard` plugin, the Dashboard uses the `type` to figure out where to mount different UI elements. `'acquirer'`-type plugins are mounted into the tab bar, while `'progressindicator'`-type plugins are mounted into the progress bar area.
 
 The plugin constructor receives the Uppy instance in the first parameter, and any options passed to `uppy.use()` in the second parameter.
 
@@ -59,17 +58,17 @@ uninstall () {
 
 ### `update(state)`
 
-Called on each state update. It's rare that you'd need to use this, it's mostly handy if you want to build a UI plugin using something other than preact.
+Called on each state update. You will rarely need to use this, it is mostly handy if you want to build a UI plugin using something other than Preact.
 
 ## Upload Hooks
 
-When creating an upload, Uppy runs files through an upload pipeline. The pipeline consists of three parts, each of which can be hooked into: Preprocessing, Uploading, and Postprocessing. Preprocessors can be used to configure uploader plugins, encrypt files, resize images, etc., before uploading them. Uploaders do the actual uploading work, such as creating an XMLHttpRequest object and sending the file. Postprocessors do work after files have been uploaded completely. This could be anything from waiting for a file to propagate across a CDN, to sending another request to relate some metadata to the file.
+When creating an upload, Uppy runs files through an upload pipeline. The pipeline consists of three parts, each of which can be hooked into: Preprocessing, Uploading, and Postprocessing. Preprocessors can be used to configure uploader plugins, encrypt files, resize images, etc., before uploading them. Uploaders do the actual uploading work, such as creating an XMLHttpRequest object and sending the file. Postprocessors do their work after files have been uploaded completely. This could be anything from waiting for a file to propagate across a CDN, to sending another request to relate some metadata to the file.
 
 Each hook is a function that receives an array containing the file IDs that are being uploaded, and returns a Promise to signal completion. Hooks are added and removed through `Uppy` methods: `addPreProcessor`, `addUploader`, `addPostProcessor`, and their `remove*` counterparts. Normally, hooks should be added during the plugin's `install()` method, and removed during the `uninstall()` method.
 
 Additionally, upload hooks can fire events to signal progress.
 
-When adding hooks, make sure to bind the hook `fn` beforehand! Otherwise it will be impossible to remove. For example:
+When adding hooks, make sure to bind the hook `fn` beforehand! Otherwise, it will be impossible to remove. For example:
 
 ```js
 class MyPlugin extends Plugin {
@@ -102,7 +101,7 @@ Add a preprocessing function. `fn` gets called with a list of file IDs before an
 
 Add an uploader function. `fn` gets called with a list of file IDs when an upload should start. Uploader functions should do the actual uploading work, such as creating and sending an XMLHttpRequest or calling into some upload service's SDK. `fn` should return a Promise that resolves once all files have been uploaded.
 
-You may choose to still resolve the Promise if some file uploads fail. This way any postprocessing will still run on the files that were uploaded successfully, while uploads that failed will be retried when `uppy.retryAll` is called.
+You may choose to still resolve the Promise if some file uploads fail. This way, any postprocessing will still run on the files that were uploaded successfully, while uploads that failed will be retried when `uppy.retryAll` is called.
 
 ### `addPostProcessor(fn)`
 
@@ -110,13 +109,13 @@ Add a postprocessing function. `fn` is called with a list of file IDs when an up
 
 ### `removePreProcessor/removeUploader/removePostProcessor(fn)`
 
-Remove a processor or uploader function that was added previously. Normally this should be done in the `uninstall()` method.
+Remove a processor or uploader function that was added previously. Normally, this should be done in the `uninstall()` method.
 
-## Progress Events
+## Progress events
 
 Progress events can be fired for individual files to show feedback to the user. For upload progress events, only emitting how many bytes are expected and how many have been uploaded is enough. Uppy will handle calculating progress percentages, upload speed, etc.
 
-Preprocessing and postprocessing progress events are plugin-dependent and can refer to anything, so Uppy doesn't try to be smart about them. There are two types of processing progress events: determinate and indeterminate. Some processing does not have meaningful progress beyond "not done" and "done". For example, sending a request to initialize a server-side resource that will be uploaded to. In those situations, indeterminate progress is suitable. Other processing does have meaningful progress. For example, encrypting a large file. In those situations, determinate progress is suitable.
+Preprocessing and postprocessing progress events are plugin-dependent and can refer to anything, so Uppy doesn't try to be smart about them. There are two types of processing progress events: determinate and indeterminate. Some processing does not have meaningful progress beyond "not done" and "done". For example, sending a request to initialize a server-side resource that will serve as the upload destination. In those situations, indeterminate progress is suitable. Other processing does have meaningful progress. For example, encrypting a large file. In those situations, determinate progress is suitable.
 
 ### `preprocess-progress(fileID, progress)`
 
@@ -163,14 +162,14 @@ This method can be overridden to support for different render engines.
 
 ### `render()`
 
-Render this plugin's UI. Uppy uses [preact](https://preactjs.com) as its view engine, so `render()` should return a preact element.
+Render this plugin's UI. Uppy uses [Preact](https://preactjs.com) as its view engine, so `render()` should return a Preact element.
 `render` is automatically called by Uppy on each state change.
 
-Note that we are looking into ways to make Uppy render engine agnostic, so that plugins can choose their own favourite library—whether it's preact, choo, jQuery, or anything else. This means that the `render()` API may change in the future, but we'll detail exactly what you need to do on the [blog](https://uppy.io/blog) if and when that happens.
+Note that we are looking into ways to make Uppy's render engine agnostic, so that plugins can choose their own favourite library—whether it's Preact, Choo, jQuery, or anything else. This means that the `render()` API may change in the future, but we will detail exactly what you need to do on the [blog](https://uppy.io/blog) if and when that happens.
 
 ### JSX
 
-Since Uppy uses Preact and not React, the default Babel configuration for JSX elements does not work. You have to import the preact `h` function and tell Babel to use it by adding a `/** @jsx h */` comment at the top of the file.
+Since Uppy uses Preact and not React, the default Babel configuration for JSX elements does not work. You have to import the Preact `h` function and tell Babel to use it by adding a `/** @jsx h */` comment at the top of the file.
 
 See the Preact [Getting Started Guide](https://preactjs.com/guide/getting-started) for more on Babel and JSX.
 

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

@@ -1,10 +1,10 @@
-<!-- Basic Uppy styles. You can use Transloadit's CDN, Edgly: 
+<!-- Basic Uppy styles. You can use Transloadit's CDN, Edgly:
 https://transloadit.edgly.net/releases/uppy/v0.25.0/dist/uppy.min.css -->
 <link rel="stylesheet" href="/uppy/uppy.min.css">
 
 <div class="UppyDragDrop"></div>
 
-<!-- Load Uppy pre-built bundled version. You can use Transloadit's CDN, Edgly: 
+<!-- Load Uppy pre-built bundled version. You can use Transloadit's CDN, Edgly:
 https://transloadit.edgly.net/releases/uppy/v0.25.0/dist/uppy.min.js -->
 <script src="/uppy/uppy.min.js"></script>
 <script>
@@ -13,7 +13,7 @@ https://transloadit.edgly.net/releases/uppy/v0.25.0/dist/uppy.min.js -->
     target: '.UppyDragDrop',
     locale: {
       strings: {
-        dropHereOr: 'Перенесите файлы сюда или',
+        dropHereOr: 'Перенесите файлы сюда или %{browse}',
         browse: 'выберите'
       }
     }

+ 1 - 1
website/src/frontpage-code-sample.ejs

@@ -9,4 +9,4 @@ save it as a layout partial
 $ npm install uppy
 {% endcodeblock %}
 
-{% include_code lang:js ../api-usage-example.ejs %}
+{% include_code lang:js ../api-usage-example.js %}

+ 11 - 2
website/themes/uppy/layout/partials/frontpage-code-sample.html

@@ -2,6 +2,15 @@
 You can `npm run web:update:frontpage:code:sample` to render this code snippet and
 save it as a layout partial
 -->
-<figure class="highlight bash"><table><tr><td class="code"><pre><div class="line">$ npm install uppy</div></pre></td></tr></table></figure>
+<figure class="highlight bash"><table><tr><td class="code"><pre>$ npm install uppy</pre></td></tr></table></figure>
 
-<figure class="highlight js"><figcaption><span>api-usage-example.ejs</span><a href="/examples/../api-usage-example.ejs">view raw</a></figcaption><table><tr><td class="code"><pre><div class="line"><span class="keyword">import</span> Uppy <span class="keyword">from</span> <span class="string">'uppy/lib/core'</span></div><div class="line"><span class="keyword">import</span> Dashboard <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/Dashboard'</span></div><div class="line"><span class="keyword">import</span> Tus <span class="keyword">from</span> <span class="string">'uppy/lib/plugins/Tus'</span></div><div class="line"> </div><div class="line">Uppy({ autoProceed: <span class="literal">false</span> })</div><div class="line">  .use(Dashboard, { trigger: <span class="string">'#select-files'</span> })</div><div class="line">  .use(Tus, { endpoint: <span class="string">'https://master.tus.io/files/'</span> })</div><div class="line">  .run()</div><div class="line">  .on(<span class="string">'complete'</span>, (result) =&gt; {</div><div class="line">    <span class="built_in">console</span>.log(<span class="string">'Upload result:'</span>, result)</div><div class="line">  })</div></pre></td></tr></table></figure>
+<figure class="highlight js"><table><tr><td class="code"><pre><span class="token keyword">import</span> Uppy <span class="token keyword">from</span> <span class="token string">'uppy/lib/core'</span>
+<span class="token keyword">import</span> Dashboard <span class="token keyword">from</span> <span class="token string">'uppy/lib/plugins/Dashboard'</span>
+<span class="token keyword">import</span> Tus <span class="token keyword">from</span> <span class="token string">'uppy/lib/plugins/Tus'</span>
+ 
+<span class="token function">Uppy</span><span class="token punctuation">(</span><span class="token punctuation">{</span> autoProceed<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
+  <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>Dashboard<span class="token punctuation">,</span> <span class="token punctuation">{</span> trigger<span class="token punctuation">:</span> <span class="token string">'#select-files'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
+  <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>Tus<span class="token punctuation">,</span> <span class="token punctuation">{</span> endpoint<span class="token punctuation">:</span> <span class="token string">'https://master.tus.io/files/'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
+  <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'complete'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>result<span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
+    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Upload result:'</span><span class="token punctuation">,</span> result<span class="token punctuation">)</span>
+  <span class="token punctuation">}</span><span class="token punctuation">)</span></pre></td></tr></table></figure>

+ 16 - 97
website/themes/uppy/source/css/_syntax.scss

@@ -4,101 +4,20 @@
 
 pre {
   color: #525252;
-
-  .function .keyword,
-  .constant {
-    color: #0092db;
-  }
-
-  .line {
-    min-height: 1em;
-  }
-
-  .keyword,
-  .attribute {
-    color: #e96900;
-  }
-
-  .number,
-  .literal {
-    color: #AE81FF;
-  }
-
-  .tag,
-  .tag .title,
-  .change,
-  .winutils,
-  .flow,
-  .lisp .title,
-  .clojure .built_in,
-  .nginx .title,
-  .tex .special {
-    color: #2973b7;
-  }
-
-  .class .title {
-    color: white;
-  }
-
-  .symbol,
-  .symbol .string,
-  .value,
-  .regexp {
-    color: $color-green;
-  }
-
-  .title {
-    color: #A6E22E;
-  }
-
-  .tag .value,
-  .string,
-  .subst,
-  .haskell .type,
-  .preprocessor,
-  .ruby .class .parent,
-  .built_in,
-  .sql .aggregate,
-  .django .template_tag,
-  .django .variable,
-  .smalltalk .class,
-  .javadoc,
-  .django .filter .argument,
-  .smalltalk .localvars,
-  .smalltalk .array,
-  .attr_selector,
-  .pseudo,
-  .addition,
-  .stream,
-  .envvar,
-  .apache .tag,
-  .apache .cbracket,
-  .tex .command,
-  .prompt {
-    color: $color-green;
-  }
-
-  .comment,
-  .java .annotation,
-  .python .decorator,
-  .template_comment,
-  .pi,
-  .doctype,
-  .deletion,
-  .shebang,
-  .apache .sqbracket,
-  .tex .formula {
-    color: #b3b3b3;
-  }
-
-  .coffeescript .javascript,
-  .javascript .xml,
-  .tex .formula,
-  .xml .javascript,
-  .xml .vbscript,
-  .xml .css,
-  .xml .cdata {
-    opacity: 0.5
-  }
-
+  // background-color: #f8f8f8 !important;
+  // font-size: .8em !important;
+  // .token.function, .token.punctuation { color: inherit; }
+
+  // Generic
+  .token.comment { color: #b3b3b3; }
+
+  // JS
+  .token.keyword { color: #e96900; }
+  .token.number, .token.boolean { color: #AE81FF; }
+  .token.class-name { color: #2973b7; }
+  .token.string { color: $color-green; }
+
+  // HTML
+  .token.tag { color: #2973b7; }
+  .token.attr-value { color: $color-green; }
 }