{"_id":"js-mass-upload","_rev":"27-2b5c0eae1447affee6472f3091d7cbbb","name":"js-mass-upload","description":"Create upload UIs for thousands of files and gigabytes of data","dist-tags":{"latest":"0.2.0"},"versions":{"0.0.6":{"name":"js-mass-upload","version":"0.0.6","description":"Create upload UIs for thousands of files and gigabytes of data","scripts":{},"repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"grunt":"~0.4.1","grunt-contrib-coffee":"~0.7.0","grunt-requirejs":"~0.4.0","karma-script-launcher":"~0.1.0","karma-chrome-launcher":"~0.1.0","karma-firefox-launcher":"~0.1.0","karma-html2js-preprocessor":"~0.1.0","karma-jasmine":"~0.1.0","karma-requirejs":"~0.1.0","karma-coffee-preprocessor":"~0.1.0","karma-phantomjs-launcher":"~0.1.0","karma":"~0.10.0","grunt-karma":"~0.6.1","grunt-contrib-watch":"~0.5.1","karma-growl-reporter":"~0.1.0","grunt-contrib-connect":"~0.3.0","grunt-contrib-less":"~0.7.0"},"bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.0.6","dist":{"shasum":"4e7fc9ffd68ca4836be424471971b7f7d12bc675","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.0.6.tgz","integrity":"sha512-bKkvHUwMVewX5UxjaujSFlEYhVVRK+G7bo3VpiPZOGONnNdFfMio/NoOE4Lot9AkcZkbye8n6bw2Kr7kqGpwCQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH7iN5U/bi+k38LLsSO5u73RUdGNqSJkpxC1iiKPmvVlAiA2enUNoZ1Ga1k202UJCtTfdAKzcORrGilkHH0ROAS0kA=="}]},"_from":".","_npmVersion":"1.3.14","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}]},"0.0.7":{"name":"js-mass-upload","version":"0.0.7","description":"Create upload UIs for thousands of files and gigabytes of data","scripts":{},"repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"grunt":"~0.4.1","grunt-contrib-coffee":"~0.7.0","grunt-requirejs":"~0.4.0","karma-script-launcher":"~0.1.0","karma-chrome-launcher":"~0.1.0","karma-firefox-launcher":"~0.1.0","karma-html2js-preprocessor":"~0.1.0","karma-jasmine":"~0.1.0","karma-requirejs":"~0.1.0","karma-coffee-preprocessor":"~0.1.0","karma-phantomjs-launcher":"~0.1.0","karma":"~0.10.0","grunt-karma":"~0.6.1","grunt-contrib-watch":"~0.5.1","karma-growl-reporter":"~0.1.0","grunt-contrib-connect":"~0.3.0","grunt-contrib-less":"~0.7.0"},"bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.0.7","dist":{"shasum":"64e6edbfc97fda246a7bc7b45de5f0eea5355ee2","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.0.7.tgz","integrity":"sha512-Kt+ys8ZlywwCtq6bbpUkCnWKvqOtr2hpePlgmxu2G+tMJI7fJmBZBj2iS7RMLcCJHEkTUhLbEfleOl4R+JZJvw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGVsMTNiaYhPdHMy4VD5ojoA1xrGRo3gryIwpATvKDzWAiEAvcIJz2TowqGA4vKu79u4AbiW7Bt+rr3lwkYwA5zT/5E="}]},"_from":".","_npmVersion":"1.3.14","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}]},"0.1.0":{"name":"js-mass-upload","version":"0.1.0","description":"Create upload UIs for thousands of files and gigabytes of data","scripts":{},"repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"backbone":"^1.1.2","browserify":"^8.1.3","chai":"^1.10.0","coffee-script":"^1.9.0","coffeeify":"^1.0.0","gulp":"^3.8.10","gulp-derequire":"^2.0.0","gulp-rename":"^1.2.0","gulp-rimraf":"^0.1.1","gulp-uglify":"^1.1.0","gulp-util":"^3.0.3","jquery":"^2.1.3","karma":"^0.12.31","karma-browserify":"^3.0.1","karma-chai":"^0.1.0","karma-mocha":"^0.1.10","karma-phantomjs-launcher":"^0.1.4","mocha":"^2.1.0","sinon":"^1.12.2","sinon-chai":"^2.6.0","underscore":"^1.7.0","vinyl-source-stream":"^1.0.0"},"gitHead":"a1e4006de1781ceb37890d4d0ad8ee77b11d8853","bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.1.0","_shasum":"64e1f751e1797b1cd07762a3d58ca2888aaf42fd","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"dist":{"shasum":"64e1f751e1797b1cd07762a3d58ca2888aaf42fd","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.1.0.tgz","integrity":"sha512-qGncDjekeA4e9IcZFBI/UXe71eugRSHp23LEWocpfoRvg1YBeSg/y/YRSGJq1/quz/G7N9WfgR6VkLNbuODPxQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4jegGsQnE0K8oZ67Fx0TI8/8pg1ZWKW9KBlJlYcBKzAIgAfhCDHsBfCMCOKh4ONKwTJgGxtw7ihlcROt4a01RmC4="}]}},"0.1.1":{"name":"js-mass-upload","version":"0.1.1","description":"Create upload UIs for thousands of files and gigabytes of data","main":"js-mass-upload.js","repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"backbone":"^1.1.2","browserify":"^8.1.3","chai":"^1.10.0","coffee-script":"^1.9.0","coffeeify":"^1.0.0","gulp":"^3.8.10","gulp-derequire":"^2.0.0","gulp-rename":"^1.2.0","gulp-rimraf":"^0.1.1","gulp-uglify":"^1.1.0","gulp-util":"^3.0.3","jquery":"^2.1.3","karma":"^0.12.31","karma-browserify":"^3.0.1","karma-chai":"^0.1.0","karma-mocha":"^0.1.10","karma-phantomjs-launcher":"^0.1.4","mocha":"^2.1.0","sinon":"^1.12.2","sinon-chai":"^2.6.0","underscore":"^1.7.0","vinyl-source-stream":"^1.0.0"},"gitHead":"60f3426bb14c6eeba758830e657b8b0212d0cf09","bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.1.1","scripts":{},"_shasum":"34c41dc6cbe053460b61f6bc8b278739edf9645e","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"dist":{"shasum":"34c41dc6cbe053460b61f6bc8b278739edf9645e","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.1.1.tgz","integrity":"sha512-CsU8XLLGnJarTQB4mS6o0uvn76OWPfZrpa7/WfpvgHKg+SP2okWCNNqbh8i5nw0bEbFv6di8tkfoD6ps2bTd1w==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDbgBnKlEJ0iwe+BHkwAfGqrtcMV3p8tKMAcB2iQTRlpwIhAODioWFm9qWCXQSzGCcG8RHrZNpAwZlPtJqLZwqV017z"}]}},"0.1.2":{"name":"js-mass-upload","version":"0.1.2","description":"Create upload UIs for thousands of files and gigabytes of data","main":"js-mass-upload.js","repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"backbone":"^1.1.2","browserify":"^8.1.3","chai":"^1.10.0","coffee-script":"^1.9.0","coffeeify":"^1.0.0","gulp":"^3.8.10","gulp-derequire":"^2.0.0","gulp-rename":"^1.2.0","gulp-rimraf":"^0.1.1","gulp-uglify":"^1.1.0","gulp-util":"^3.0.3","jquery":"^2.1.3","karma":"^0.12.31","karma-browserify":"^3.0.1","karma-chai":"^0.1.0","karma-mocha":"^0.1.10","karma-phantomjs-launcher":"^0.1.4","mocha":"^2.1.0","sinon":"^1.12.2","sinon-chai":"^2.6.0","underscore":"^1.7.0","vinyl-source-stream":"^1.0.0"},"gitHead":"9fc2da8e0e56353c4ed03f9eaf45e97bc6f02250","bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.1.2","scripts":{},"_shasum":"ccb534ce5ce2509d488c73c622420010d956b65a","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"dist":{"shasum":"ccb534ce5ce2509d488c73c622420010d956b65a","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.1.2.tgz","integrity":"sha512-xTVjAMm04/+LJao0utkyHR1oYVbeJNjCTKxYBiruPlhK2ZyGasffBfIkON5hYyD4GNuFmqYpS5NFAT9+jfBiGg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF5fGxf1dq+ghBygfe557Glh5xgkQBchWNldMQ3kpbMpAiEArDTT1Yo2zozwPnBdfBWuR+nqMVvVC3NC20Xg5TQskzg="}]}},"0.1.3":{"name":"js-mass-upload","version":"0.1.3","description":"Create upload UIs for thousands of files and gigabytes of data","main":"js-mass-upload.js","repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"backbone":"^1.1.2","browserify":"^8.1.3","chai":"^1.10.0","coffee-script":"^1.9.0","coffeeify":"^1.0.0","gulp":"^3.8.10","gulp-derequire":"^2.0.0","gulp-rename":"^1.2.0","gulp-rimraf":"^0.1.1","gulp-uglify":"^1.1.0","gulp-util":"^3.0.3","jquery":"^2.1.3","karma":"^0.12.31","karma-browserify":"^3.0.1","karma-chai":"^0.1.0","karma-mocha":"^0.1.10","karma-phantomjs-launcher":"^0.1.4","mocha":"^2.1.0","sinon":"^1.12.2","sinon-chai":"^2.6.0","underscore":"^1.7.0","vinyl-source-stream":"^1.0.0"},"gitHead":"f0b2eab456db996e1e7773f6c5b5790dcf46be8a","bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.1.3","scripts":{},"_shasum":"fd1ed1a63b8a11241c24d5818db06d6016264c65","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"dist":{"shasum":"fd1ed1a63b8a11241c24d5818db06d6016264c65","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.1.3.tgz","integrity":"sha512-5poDbJ5xN45nmHXvWOm+AYfBhWGdgM8fLndjAelb5k49jXU1l/ghb57Y7KN+wR3osB4wzcPNqIXYT1GvDAzMqw==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCCr2Jno8dwpanXdXY2cLXhRxJ6eVQHIxXKFfmIN+CQQIgRmFv7PvhTnqlniUam956oXgrFIYNV4Qq48DtKGtOIpc="}]}},"0.1.4":{"name":"js-mass-upload","version":"0.1.4","description":"Create upload UIs for thousands of files and gigabytes of data","main":"js-mass-upload.js","repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"backbone":"^1.1.2","browserify":"^8.1.3","chai":"^1.10.0","coffee-script":"^1.9.0","coffeeify":"^1.0.0","gulp":"^3.8.10","gulp-derequire":"^2.0.0","gulp-rename":"^1.2.0","gulp-rimraf":"^0.1.1","gulp-uglify":"^1.1.0","gulp-util":"^3.0.3","jquery":"^2.1.3","karma":"^0.12.31","karma-browserify":"^3.0.1","karma-chai":"^0.1.0","karma-mocha":"^0.1.10","karma-phantomjs-launcher":"^0.1.4","mocha":"^2.1.0","sinon":"^1.12.2","sinon-chai":"^2.6.0","underscore":"^1.7.0","vinyl-source-stream":"^1.0.0"},"gitHead":"f5cadf718d0661372069443204d76f84e6e32c8b","bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.1.4","scripts":{},"_shasum":"f9a66080ab0daf69a4e0cf3306a095a3722f7c14","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"dist":{"shasum":"f9a66080ab0daf69a4e0cf3306a095a3722f7c14","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.1.4.tgz","integrity":"sha512-DSdS+fTN6YzJnUZeojAWM940plPOkX6pKbGB3+yO2i+IEMqabwBqrd9yKQtF2GGSM2boUNa7DqHeAy6ro/MTSg==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDjBeHrKkDoFrP8rYoS0tBiPW7IejLLdO4l/prSlSf5JAiAcO5tcb1dsQgKXKUPF0YQwrH8q6jRqv5pqn1AFzuWVwA=="}]}},"0.2.0":{"name":"js-mass-upload","version":"0.2.0","description":"Create upload UIs for thousands of files and gigabytes of data","main":"js-mass-upload.js","repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"keywords":["mass","upload","file","blob","ui"],"author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"licenses":[{"type":"Apache","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}],"devDependencies":{"backbone":"^1.1.2","browserify":"^10.1.3","chai":"^2.3.0","coffee-script":"^1.9.2","coffeeify":"^1.1.0","gulp":"^3.8.11","gulp-derequire":"^2.1.0","gulp-rename":"^1.2.2","gulp-rimraf":"^0.1.1","gulp-uglify":"^1.2.0","gulp-util":"^3.0.4","jquery":"^2.1.4","karma":"^0.12.31","karma-browserify":"^4.1.2","karma-chai":"^0.1.0","karma-mocha":"^0.1.10","karma-phantomjs-launcher":"^0.1.4","mocha":"^2.2.4","sinon":"^1.14.1","sinon-chai":"^2.7.0","underscore":"^1.8.3","vinyl-source-stream":"^1.1.0"},"gitHead":"6284611ecc8cb274b36de28ffb4fcd7c9691214e","bugs":{"url":"https://github.com/overview/js-mass-upload/issues"},"homepage":"https://github.com/overview/js-mass-upload","_id":"js-mass-upload@0.2.0","scripts":{},"_shasum":"01042d0f6d819e64b96da37db7a8edabdd171667","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"adamhooper","email":"adam@adamhooper.com"},"maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"dist":{"shasum":"01042d0f6d819e64b96da37db7a8edabdd171667","tarball":"https://registry.npmjs.org/js-mass-upload/-/js-mass-upload-0.2.0.tgz","integrity":"sha512-wnL7ppFucLtO3Xk9L/cp/EMjJrd3YfMyQAfEyKC9X2Np1N7rajoJaNYn6PfVbsgRY4Hu6EZyx+vHaNu5lwPkuQ==","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDx0HjpPazqcFZB0QaElndZulyqdIjZHcy+Qd9A9QRtfwIgQfwOA0L+DZGxK+6kROV0GDRLwJ9F5LOSAFAU2oNC09E="}]}}},"readme":"MassUpload\n==========\n\nLets the user upload lots of huge files.\n\nWhy?\n----\n\nThere are lots of libraries that do this already, but they're missing a key question: how do we upload a file to the server? This is important: as you go beyond tiny files, `multipart/form-data` just won't cut it.\n\nMassUpload lets the developer decide. So you, being a clever developer, can upload several gigabytes' worth of data and resume when interrupted.\n\nUsage\n-----\n\nFirst, include the library. (It depends on [Backbone](http://backbonejs.org).) You may use [RequireJs](http://requirejs.org):\n\n```javascript\nrequire([ 'path/to/mass-upload' ], function(MassUpload) { ... });\n```\n\nOr you can use [Browserify](http://browserify.org/) or plain Node `require`:\n\n```javascript\nvar MassUpload = require('js-mass-upload');\n```\n\nOr, you can go the old-fashioned route in your HTML:\n\n```html\n<script src=\"path/to/underscore-1.5.1.js\"></script>\n<script src=\"path/to/backbone-1.0.0.js\"></script>\n<script src=\"path/to/mass-upload.no-require.js\"></script>\n```\n\nNow you'll need to implement a few asynchronous functions. The return values are ignored; instead, these functions are expected to call the appropriate callbacks that are passed to them.\n\n```javascript\nvar app = new MassUpload({\n  doListFiles: function(progress, done) { ... },\n  doUploadFile: function(upload, progress, done) { ... },\n  doDeleteFile: function(upload, done) { ... },\n});\n\napp.on('change:status', function() { ... });\napp.on('change:listFilesProgress', function() { ... });\napp.on('change:uploadProgress', function() { ... });\n\n$('form#mass-upload').prepend(app.el);\n\napp.fetchFileInfosFromServer() // kicks everything off\n```\n\nFinally, write your UI. It may iterate over `MassUpload.uploads`, a list of files; `onStateChanged` will be called whenever the list changes (because the total size of the upload will change).\n\nBroad ideas\n-----------\n\nIf you're going to support resume (or even if you aren't but you want to keep yourself open to the possibility some day), then the user interface could get tricky: how do you get users to re-select the files they selected last time they were on this page?\n\nThe answer is to be transparent:\n\n* Keep a list on the server of what the user has uploaded.\n* Show that list to the user.\n* Let the user edit that list, by deleting files or adding new files.\n\nMassUpload does this.\n\nClient-side API\n---------------\n\nYou'll need to implement a few functions and pass them in the MassUpload app's constructor.\n\nBut first let's define some datatypes:\n\n* `File`: See the [W3 File API](http://www.w3.org/TR/FileAPI/#dfn-file). This represents a file on the client side. The web browser creates this object.\n* `ProgressEvent`: See the [W3 Progress Events API](http://www.w3.org/TR/progress-events/).\n* `MassUpload`: an object containing:\n    * `state` (a `State`)\n    * `uploads` (an Array of `Upload` objects)\n    * `deleteUpload(upload)`: deletes an upload\n* `MassUpload.FileInfo`: depicts a file on the server. An object containing:\n    * `name` (a `String`)\n    * `lastModifiedDate` (a `Date`)\n    * `total` (an integer `Number` depicting total size)\n    * `loaded` (an integer `Number` less than or equal to `size`)\n* `MassUpload.Upload`: depicts a single object which either *is* on the server or *should* be on the server. An object containing:\n    * `file` (a `File`, if the user has selected this file on the client)\n    * `fileInfo` (a `FileInfo`, if the file is on the server, either partially uploaded or completely uploaded)\n    * `error` (an `Error` or `null`)\n    * `isComplete()`: `true` iff `fileInfo.loaded === fileInfo.total`\n* `MassUpload.State`: an immutable object containing:\n    * `loaded` and `total` (integer `Number`s, like in a `ProgressEvent`)\n    * `status` (`\"listing\"`, `\"uploading\"` or `\"waiting\"`)\n    * `errors` (an Array of `Error` objects)\n    * `isComplete()`: `true` iff `state === \"waiting\" && loaded === total && total > 0 && errors.length === 0`\n* `MassUpload.Error`: an error that prevents this upload from going smoothly. Errors can be removed by retrying operations or removing the files that they apply to. They are immutable and contain:\n    * `failedCall`: \"listFiles\", \"uploadFile\" or \"deleteFile\"\n    * `failedCallArgument`: `undefined`, a `File` or a `FileInfo`, as appropriate\n    * `detail`: a raw error object (you define what that should be; perhaps an `XMLHttpRequest`)\n\nGot that? Great. Here's what you need to implement:\n\n---\n\n```javascript\nfunction doListFiles(progress, done) { ... }\n```\n\nLists files already uploaded to the server. Called when initializing.\n\n* Calls `progress(progressEvent)`, where `progressEvent` is a `ProgressEvent`, during loading.\n* Calls `done(null, data)`, where `data` is an `Array` of `FileInfo` objects, when loading is complete.\n* Calls `done(error)`, where `error` is an `Error` (perhaps with a `.xhr` property), if listing fails.\n\n**Parameters**:\n\n* `progress`: To be called during loading.\n* `done`: To be called on error or after completion.\n\n---\n\n```javascript\nfunction doUploadFile(upload, progress, done) { ... }\n```\n\nUploads a file to the server. Called asynchronously.\n\n* Calls `progress(progressEvent)`, where `progressEvent` is a `ProgressEvent`, during loading.\n* Calls `done(null)` when upload is complete.\n* Calls `done(error)`, where `error` is an `Error` (perhaps with a `.xhr` property), if listing fails.\n\n**Parameters**:\n\n* `upload`: An `Upload`. Call `upload.get('file')` to get its `File`. The `File` may already be partially uploaded to the server.\n* `progress`: To be called during upload.\n* `done`: To be called on error or after completion.\n\n**Returns**: a function that, when called, aborts the operation as soon as possible. (The `abort` function should asynchronously call `done(new Error('aborted'))`.)\n\n---\n\n```javascript\nfunction doDeleteFile(upload, success, error) { ... }\n```\n\nDeletes a file from the server. Called asynchronously.\n\n* Calls `done(null)` when the delete is complete.\n* Calls `done(error)`, where `error` is an `Error` (perhaps with a `.xhr` property), if deleting fails.\n\n**Parameters**:\n\n* `upload`: An `Upload`. Call `upload.get('fileInfo')` to get its `FileInfo` object.\n* `done`: To be called on error or after completion.\n\n---\n\n```javascript\nfunction onStateChanged(state, massUpload) { ... }\n```\n\nDoes something when the state changes.\n\nIn practical terms, callbacks should listen and:\n\n* Display global progress.\n* Display error messages (both global and alongside `File` and `FileInfo` representations).\n* Prompt the user to resolve errors.\n* Submit the form when upload is complete.\n\n**Parameters**:\n\n* `state`, a `State`.\n* `massUpload`, a `MassUpload`.\n\nTalking with a server\n---------------------\n\nLet's portray an example web service in which the user owns a bunch of folders. For each folder, the user owns some files.\n\nLet's give each file a [GUID](http://en.wikipedia.org/wiki/Globally_unique_identifier) based on its filename, size and lastModifiedDate.\n\nLet's also assume we want to be able to resume uploads. We can't do that with multipart/form-data; instead, we'll upload raw binary blobs.\n\nOur server API might look something like this:\n\n| Method | URL | Parameters | Description |\n| ------ | --- | ---------- | ----------- |\n| `GET` | `/folders` | | Lists folders |\n| `POST` | `/folders` | `name`, `permalink` | Creates a folder |\n| `DELETE` | `/folders/:permalink` | | Deletes a folder |\n| `PUT` | `/folders/:permalink` | `name` | Renames a folder |\n| `GET` | `/folders/:permalink/files` | | Lists a folder's contents |\n| `DELETE` | `/folders/:permalink/files` | | Deletes all files from the folder\n| `PUT` | `/folders/:permalink/files/:guid` | `name`, `size`, `lastModifiedDate` | Creates an empty file with the given GUID\n| `HEAD` | `/folders/:permalink/files/:guid` | | Describes how much of the file is uploaded, using a [`Content-Range`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16) header.\n| `PATCH` | `/folders/:permalink/files/:guid` | (raw bytes) | Uploads the file's raw bytes using the passed `Content-Range` header. Fails if the `Content-Range` would create a gap in the file or go past the end of the file; succeeds once the file is completely uploaded.\n| `DELETE` | `/folders/:permalink/files/:guid` | | Deletes the file from the server\n\nThen our client-side API methods should do something like this:\n\n| Function | Behavior |\n| -------- | -------- |\n| `doListFiles(progress, done)` | Mirrors `GET /folders/:permalink/files` |\n| `doUploadFile(file, progress, done)` | Determines `guid`; calls `HEAD /folders/:permalink/files/:guid`, optionally calls `PUT /folders/:permalink/files/:guid` if the file does not exist; calculates the missing `Content-Range` and calls `file.slice()` to create a [`Blob`](http://www.w3.org/TR/FileAPI/#dfn-Blob) starting at that range; calls `PATCH /folders/:permalink/files/:guid` sending the `Blob`. (In `jQuery.ajax()`, you can send a blob using `processData: false`.) |\n| `doDeleteFile(fileInfo, done)` | Determines `guid`; calls `DELETE /folders/:permalink/files/:guid`; calls success if the server responds with success or `404`, otherwise calls error. |\n\nContributing\n------------\n\nTo build and contribute:\n\n1. Install [NodeJS](http://nodejs.org/)\n2. Clone this repository: `git clone https://github.com/overview/js-mass-upload.git`\n3. `npm install` in this directory\n4. `gulp` to build\n5. `gulp test` to unit-test\n6. Add a test; return to step 4; make test pass; return to step 4\n7. git commit and create a pull request\n\nLicense\n-------\n\nCopyright 2013 The Overview Project\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","maintainers":[{"name":"adamhooper","email":"adam@adamhooper.com"}],"time":{"modified":"2022-06-19T05:38:16.812Z","created":"2014-02-03T17:34:50.065Z","0.0.6":"2014-02-03T17:39:17.679Z","0.0.7":"2014-07-14T21:45:58.190Z","0.1.0":"2015-02-02T21:01:46.530Z","0.1.1":"2015-02-02T21:03:17.152Z","0.1.2":"2015-02-02T21:29:30.502Z","0.1.3":"2015-02-02T21:31:31.444Z","0.1.4":"2015-02-02T21:33:16.637Z","0.2.0":"2015-05-12T14:48:34.255Z"},"readmeFilename":"README.md","author":{"name":"The Overview Project","email":"adam@adamhooper.com"},"repository":{"type":"git","url":"https://github.com/overview/js-mass-upload"},"homepage":"https://github.com/overview/js-mass-upload","keywords":["mass","upload","file","blob","ui"],"bugs":{"url":"https://github.com/overview/js-mass-upload/issues"}}