diff --git a/ChangeLog b/ChangeLog
index 5fb1db6c..124eca45 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -73,6 +73,8 @@ keyStop: function(e, opt) {
* Fixed bug with setting curent cursor when
clicked on menu item.
+* Updated dropbox library to v.0.8.1.
+
2012.12.12, Version 0.1.8
diff --git a/lib/client/storage/_dropbox.js b/lib/client/storage/_dropbox.js
index 899b4b2e..9d3ba79d 100644
--- a/lib/client/storage/_dropbox.js
+++ b/lib/client/storage/_dropbox.js
@@ -17,8 +17,7 @@ var CloudCommander, Util, DOM, Dropbox, cb, Client;
function load(pCallBack){
console.time('dropbox load');
- var lSrc = '//cdnjs.cloudflare.com/ajax/libs/dropbox.js/0.7.1/dropbox.min.js',
- //var lSrc = CloudCmd.LIBDIRCLIENT + 'storage/dropbox/lib/dropbox.js',
+ var lSrc = 'http://cdnjs.cloudflare.com/ajax/libs/dropbox.js/0.8.1/dropbox.min.js',
lLocal = CloudCmd.LIBDIRCLIENT + 'storage/dropbox/lib/dropbox.min.js',
lOnload = function(){
console.timeEnd('dropbox load');
diff --git a/lib/client/storage/dropbox/.gitignore b/lib/client/storage/dropbox/.gitignore
new file mode 100644
index 00000000..49398f0b
--- /dev/null
+++ b/lib/client/storage/dropbox/.gitignore
@@ -0,0 +1,37 @@
+# Vim.
+*.swp
+
+# OSX
+.DS_Store
+
+# Npm modules.
+node_modules
+
+# Vendored javascript modules.
+test/vendor
+
+# Build output.
+lib/dropbox.js
+lib/dropbox.min.js
+test/chrome_app/lib
+test/chrome_app/manifest.json
+test/chrome_app/node_modules
+test/chrome_app/test
+test/chrome_extension/*.js
+test/chrome_profile
+test/js
+tmp/*.js
+
+# Documentation output.
+doc/*.html
+doc/assets
+doc/classes
+doc/files
+
+# Node packaging output.
+dropbox-*.tgz
+
+# Potentially sensitive credentials and keys used during testing.
+test/.token
+test/ssl
+
diff --git a/lib/client/storage/dropbox/.npmignore b/lib/client/storage/dropbox/.npmignore
new file mode 100644
index 00000000..86414dae
--- /dev/null
+++ b/lib/client/storage/dropbox/.npmignore
@@ -0,0 +1,45 @@
+# Vim.
+*.swp
+.vimrc
+
+# OSX
+.DS_Store
+
+# Git.
+.git
+
+# Npm modules.
+node_modules
+
+# Vendored javascript modules.
+test/vendor
+
+# Minified library.
+lib/dropbox.min.js
+
+# Sample apps.
+samples
+
+# Test build output.
+test/chrome_profile
+test/js
+tmp/*.js
+
+# Test code that is not related to node.js.
+test/chrome_app
+
+# Documentation output.
+doc/*.html
+doc/assets
+doc/classes
+doc/files
+
+# Test app logos.
+test/app_icon
+# Test automating Chrome extension.
+test/chrome_extension
+
+# Potentially sensitive credentials and keys used during testing.
+test/.token
+test/ssl
+
diff --git a/lib/client/storage/dropbox/txt.vimrc.txt b/lib/client/storage/dropbox/.vimrc
similarity index 100%
rename from lib/client/storage/dropbox/txt.vimrc.txt
rename to lib/client/storage/dropbox/.vimrc
diff --git a/lib/client/storage/dropbox/Cakefile b/lib/client/storage/dropbox/Cakefile
index 4a530770..17fd7a55 100644
--- a/lib/client/storage/dropbox/Cakefile
+++ b/lib/client/storage/dropbox/Cakefile
@@ -1,131 +1,215 @@
-async = require 'async'
-{spawn, exec} = require 'child_process'
-fs = require 'fs'
-log = console.log
-remove = require 'remove'
-
-# Node 0.6 compatibility hack.
-unless fs.existsSync
- path = require 'path'
- fs.existsSync = (filePath) -> path.existsSync filePath
-
-
-task 'build', ->
- build()
-
-task 'test', ->
- vendor ->
- build ->
- ssl_cert ->
- tokens ->
- run 'node_modules/mocha/bin/mocha --colors --slow 200 ' +
- '--timeout 10000 --require test/js/helper.js test/js/*test.js'
-
-task 'webtest', ->
- vendor ->
- build ->
- ssl_cert ->
- tokens ->
- webFileServer = require './test/js/web_file_server.js'
- webFileServer.openBrowser()
-
-task 'cert', ->
- remove.removeSync 'test/ssl', ignoreMissing: true
- ssl_cert()
-
-task 'vendor', ->
- remove.removeSync './test/vendor', ignoreMissing: true
- vendor()
-
-task 'tokens', ->
- remove.removeSync './test/.token', ignoreMissing: true
- build ->
- tokens ->
- process.exit 0
-
-task 'doc', ->
- run 'node_modules/codo/bin/codo src'
-
-task 'extension', ->
- run 'node_modules/coffee-script/bin/coffee ' +
- '--compile test/chrome_extension/*.coffee'
-
-build = (callback) ->
- commands = []
- # Compile without --join for decent error messages.
- commands.push 'node_modules/coffee-script/bin/coffee --output tmp ' +
- '--compile src/*.coffee'
- commands.push 'node_modules/coffee-script/bin/coffee --output lib ' +
- '--compile --join dropbox.js src/*.coffee'
- # Minify the javascript, for browser distribution.
- commands.push 'node_modules/uglify-js/bin/uglifyjs --compress --mangle ' +
- '--output lib/dropbox.min.js lib/dropbox.js'
- commands.push 'node_modules/coffee-script/bin/coffee --output test/js ' +
- '--compile test/src/*.coffee'
- async.forEachSeries commands, run, ->
- callback() if callback
-
-ssl_cert = (callback) ->
- fs.mkdirSync 'test/ssl' unless fs.existsSync 'test/ssl'
- if fs.existsSync 'test/ssl/cert.pem'
- callback() if callback?
- return
-
- run 'openssl req -new -x509 -days 365 -nodes -batch ' +
- '-out test/ssl/cert.pem -keyout test/ssl/cert.pem ' +
- '-subj /O=dropbox.js/OU=Testing/CN=localhost ', callback
-
-vendor = (callback) ->
- # All the files will be dumped here.
- fs.mkdirSync 'test/vendor' unless fs.existsSync 'test/vendor'
-
- # Embed the binary test image into a 7-bit ASCII JavaScript.
- bytes = fs.readFileSync 'test/binary/dropbox.png'
- fragments = []
- for i in [0...bytes.length]
- fragment = bytes.readUInt8(i).toString 16
- while fragment.length < 4
- fragment = '0' + fragment
- fragments.push "\\u#{fragment}"
- js = "window.testImageBytes = \"#{fragments.join('')}\";"
- fs.writeFileSync 'test/vendor/favicon.js', js
-
- downloads = [
- # chai.js ships different builds for browsers vs node.js
- ['http://chaijs.com/chai.js', 'test/vendor/chai.js'],
- # sinon.js also ships special builds for browsers
- ['http://sinonjs.org/releases/sinon.js', 'test/vendor/sinon.js'],
- # ... and sinon.js ships an IE-only module
- ['http://sinonjs.org/releases/sinon-ie.js', 'test/vendor/sinon-ie.js']
- ]
- async.forEachSeries downloads, download, ->
- callback() if callback
-
-tokens = (callback) ->
- TokenStash = require './test/js/token_stash.js'
- tokenStash = new TokenStash
- (new TokenStash()).get ->
- callback() if callback?
-
-run = (args...) ->
- for a in args
- switch typeof a
- when 'string' then command = a
- when 'object'
- if a instanceof Array then params = a
- else options = a
- when 'function' then callback = a
-
- command += ' ' + params.join ' ' if params?
- cmd = spawn '/bin/sh', ['-c', command], options
- cmd.stdout.on 'data', (data) -> process.stdout.write data
- cmd.stderr.on 'data', (data) -> process.stderr.write data
- process.on 'SIGHUP', -> cmd.kill()
- cmd.on 'exit', (code) -> callback() if callback? and code is 0
-
-download = ([url, file], callback) ->
- if fs.existsSync file
- callback() if callback?
- return
-
- run "curl -o #{file} #{url}", callback
\ No newline at end of file
+async = require 'async'
+{spawn, exec} = require 'child_process'
+fs = require 'fs'
+glob = require 'glob'
+log = console.log
+path = require 'path'
+remove = require 'remove'
+
+# Node 0.6 compatibility hack.
+unless fs.existsSync
+ fs.existsSync = (filePath) -> path.existsSync filePath
+
+
+task 'build', ->
+ build()
+
+task 'test', ->
+ vendor ->
+ build ->
+ ssl_cert ->
+ tokens ->
+ run 'node_modules/mocha/bin/mocha --colors --slow 200 ' +
+ '--timeout 10000 --require test/js/helper.js test/js/*test.js'
+
+task 'webtest', ->
+ vendor ->
+ build ->
+ ssl_cert ->
+ tokens ->
+ webtest()
+
+task 'cert', ->
+ remove.removeSync 'test/ssl', ignoreMissing: true
+ ssl_cert()
+
+task 'vendor', ->
+ remove.removeSync './test/vendor', ignoreMissing: true
+ vendor()
+
+task 'tokens', ->
+ remove.removeSync './test/.token', ignoreMissing: true
+ build ->
+ tokens ->
+ process.exit 0
+
+task 'doc', ->
+ run 'node_modules/codo/bin/codo src'
+
+task 'extension', ->
+ run 'node_modules/coffee-script/bin/coffee ' +
+ '--compile test/chrome_extension/*.coffee'
+
+task 'chrome', ->
+ vendor ->
+ build ->
+ # The v2 Chrome App API isn't supported yet.
+ buildChromeApp 'app_v1'
+
+task 'chrometest', ->
+ vendor ->
+ build ->
+ # The v2 Chrome App API isn't supported yet.
+ buildChromeApp 'app_v1', ->
+ testChromeApp()
+
+build = (callback) ->
+ commands = []
+
+ # Ignoring ".coffee" when sorting.
+ # We want "driver.coffee" to sort before "driver-browser.coffee"
+ source_files = glob.sync 'src/*.coffee'
+ source_files.sort (a, b) ->
+ a.replace(/\.coffee$/, '').localeCompare b.replace(/\.coffee$/, '')
+
+ # Compile without --join for decent error messages.
+ commands.push 'node_modules/coffee-script/bin/coffee --output tmp ' +
+ "--compile #{source_files.join(' ')}"
+ commands.push 'node_modules/coffee-script/bin/coffee --output lib ' +
+ "--compile --join dropbox.js #{source_files.join(' ')}"
+ # Minify the javascript, for browser distribution.
+ commands.push 'node_modules/uglify-js/bin/uglifyjs --compress --mangle ' +
+ '--output lib/dropbox.min.js lib/dropbox.js'
+ commands.push 'node_modules/coffee-script/bin/coffee --output test/js ' +
+ '--compile test/src/*.coffee'
+ async.forEachSeries commands, run, ->
+ callback() if callback
+
+webtest = (callback) ->
+ webFileServer = require './test/js/web_file_server.js'
+ if 'BROWSER' of process.env
+ if process.env['BROWSER'] is 'false'
+ url = webFileServer.testUrl()
+ console.log "Please open the URL below in your browser:\n #{url}"
+ else
+ webFileServer.openBrowser process.env['BROWSER']
+ else
+ webFileServer.openBrowser()
+ callback() if callback?
+
+ssl_cert = (callback) ->
+ fs.mkdirSync 'test/ssl' unless fs.existsSync 'test/ssl'
+ if fs.existsSync 'test/ssl/cert.pem'
+ callback() if callback?
+ return
+
+ run 'openssl req -new -x509 -days 365 -nodes -batch ' +
+ '-out test/ssl/cert.pem -keyout test/ssl/cert.pem ' +
+ '-subj /O=dropbox.js/OU=Testing/CN=localhost ', callback
+
+vendor = (callback) ->
+ # All the files will be dumped here.
+ fs.mkdirSync 'test/vendor' unless fs.existsSync 'test/vendor'
+
+ # Embed the binary test image into a 7-bit ASCII JavaScript.
+ bytes = fs.readFileSync 'test/binary/dropbox.png'
+ fragments = []
+ for i in [0...bytes.length]
+ fragment = bytes.readUInt8(i).toString 16
+ while fragment.length < 4
+ fragment = '0' + fragment
+ fragments.push "\\u#{fragment}"
+ js = "window.testImageBytes = \"#{fragments.join('')}\";"
+ fs.writeFileSync 'test/vendor/favicon.js', js
+
+ downloads = [
+ # chai.js ships different builds for browsers vs node.js
+ ['http://chaijs.com/chai.js', 'test/vendor/chai.js'],
+ # sinon.js also ships special builds for browsers
+ ['http://sinonjs.org/releases/sinon.js', 'test/vendor/sinon.js'],
+ # ... and sinon.js ships an IE-only module
+ ['http://sinonjs.org/releases/sinon-ie.js', 'test/vendor/sinon-ie.js']
+ ]
+ async.forEachSeries downloads, download, ->
+ callback() if callback
+
+testChromeApp = (callback) ->
+ # Clean up the profile.
+ fs.mkdirSync 'test/chrome_profile' unless fs.existsSync 'test/chrome_profile'
+
+ command = "\"#{chromeCommand()}\" --load-extension=test/chrome_app " +
+ '--user-data-dir=test/chrome_profile --no-default-browser-check ' +
+ '--no-first-run --no-service-autorun --disable-default-apps ' +
+ '--homepage=about:blank --v=-1'
+
+ run command, ->
+ callback() if callback
+
+buildChromeApp = (manifestFile, callback) ->
+ unless fs.existsSync 'test/chrome_app/test'
+ fs.mkdirSync 'test/chrome_app/test'
+ unless fs.existsSync 'test/chrome_app/node_modules'
+ fs.mkdirSync 'test/chrome_app/node_modules'
+
+ links = [
+ ['lib', 'test/chrome_app/lib'],
+ ['node_modules/mocha', 'test/chrome_app/node_modules/mocha'],
+ ['node_modules/sinon-chai', 'test/chrome_app/node_modules/sinon-chai'],
+ ['test/.token', 'test/chrome_app/test/.token'],
+ ['test/binary', 'test/chrome_app/test/binary'],
+ ['test/html', 'test/chrome_app/test/html'],
+ ['test/js', 'test/chrome_app/test/js'],
+ ['test/vendor', 'test/chrome_app/test/vendor'],
+ ]
+ commands = [
+ "cp test/chrome_app/manifests/#{manifestFile}.json " +
+ 'test/chrome_app/manifest.json'
+ ]
+ for link in links
+ # fs.symlinkSync(path.resolve(link[0]), link[1]) unless fs.existsSync link[1]
+ commands.push "cp -r #{link[0]} #{path.dirname(link[1])}"
+ async.forEachSeries commands, run, ->
+ callback() if callback
+
+chromeCommand = ->
+ paths = [
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
+ '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
+ '/Applications/Chromium.app/MacOS/Contents/Chromium',
+ ]
+ for path in paths
+ return path if fs.existsSync path
+
+ if 'process.platform' is 'win32'
+ 'chrome'
+ else
+ 'google-chrome'
+
+tokens = (callback) ->
+ TokenStash = require './test/js/token_stash.js'
+ tokenStash = new TokenStash
+ (new TokenStash()).get ->
+ callback() if callback?
+
+run = (args...) ->
+ for a in args
+ switch typeof a
+ when 'string' then command = a
+ when 'object'
+ if a instanceof Array then params = a
+ else options = a
+ when 'function' then callback = a
+
+ command += ' ' + params.join ' ' if params?
+ cmd = spawn '/bin/sh', ['-c', command], options
+ cmd.stdout.on 'data', (data) -> process.stdout.write data
+ cmd.stderr.on 'data', (data) -> process.stderr.write data
+ process.on 'SIGHUP', -> cmd.kill()
+ cmd.on 'exit', (code) -> callback() if callback? and code is 0
+
+download = ([url, file], callback) ->
+ if fs.existsSync file
+ callback() if callback?
+ return
+
+ run "curl -o #{file} #{url}", callback
diff --git a/lib/client/storage/dropbox/doc/auth_drivers.md b/lib/client/storage/dropbox/doc/auth_drivers.md
index 5507f058..14971649 100644
--- a/lib/client/storage/dropbox/doc/auth_drivers.md
+++ b/lib/client/storage/dropbox/doc/auth_drivers.md
@@ -100,7 +100,8 @@ their Dropbox. This behavior is unintuitive to users. A reasonable compromise
for apps that use `rememberUser` is to provide a `Sign out` button that calls
the `signOut` method on the app's `Dropbox.Client` instance.
-The
+The driver's constructor takes a `receiverPath` option t
+
[checkbox.js](https://github.com/dropbox/dropbox-js/tree/master/samples/checkbox.js)
sample application uses `rememberUser`, and implements signing off as described
above.
@@ -123,10 +124,12 @@ Chrome, Firefox and IE10+.
If the drawbacks above are more acceptable than restructuring your application
to handle redirects, create a page on your site that contains the
[receiver code](https://github.com/dropbox/dropbox-js/blob/master/test/html/oauth_receiver.html),
-and point the `Dropbox.Drivers.Popup` constructor to it.
+change the code to reflect the location of `dropbox.js` on your site, and point
+the `Dropbox.Drivers.Popup` constructor to it.
```javascript
-client.authDriver(new Dropbox.Drivers.Popup({receiverUrl: "https://url.to/receiver.html"}));
+client.authDriver(new Dropbox.Drivers.Popup({
+ receiverUrl: "https://url.to/oauth_receiver.html"}));
```
The popup driver adds a `#` (fragment hash) to the receiver URL if necessary,
@@ -138,9 +141,42 @@ If you have a good reason to disable the behavior above, set the `noFragment`
option to true.
```javascript
-client.authDriver(new Dropbox.Drivers.Popup({receiverUrl: "https://url.to/receiver.html", noFragment: true}));
+client.authDriver(new Dropbox.Drivers.Popup({
+ receiverUrl: "https://url.to/receiver.html", noFragment: true}));
```
+The popup driver implements the `rememberUser` option with the same semantics
+and caveats as the redirecting driver.
+
+
+### Dropbox.Drivers.Chrome
+
+Google Chrome [extensions](http://developer.chrome.com/extensions/) and
+[applications](developer.chrome.com/apps/) are supported by a driver that opens
+a new browser tab (in the case of extensions and legacy applications) or
+an application window (for new applications) to complete the OAuth
+authorization.
+
+To use this driver, first add the following files to your extension.
+
+* the [receiver script](https://github.com/dropbox/dropbox-js/blob/master/test/src/chrome_oauth_receiver.coffee);
+the file is both valid JavaScript and valid CoffeeScript
+* the [receiver page](https://github.com/dropbox/dropbox-js/blob/master/test/html/chrome_oauth_receiver.html);
+change the page to reflect the paths to `dropbox.js` and to the receiver script
+file
+
+Point the driver constructor to the receiver page:
+
+```javascript
+client.authDriver(new Dropbox.Drivers.Chrome({
+ receiverPath: "path/to/chrome_oauth_receiver.html"}));
+```
+
+This driver caches the user's credentials so that users don't have to authorize
+applications / extensions on every browser launch. Applications and extensions'
+UI should include a method for the user to sign out of Dropbox, which can be
+implemented by calling the `signOut` instance method of `Dropbox.Client`.
+
### Dropbox.Drivers.NodeServer
diff --git a/lib/client/storage/dropbox/doc/development.md b/lib/client/storage/dropbox/doc/development.md
index 1429d64b..7600a196 100644
--- a/lib/client/storage/dropbox/doc/development.md
+++ b/lib/client/storage/dropbox/doc/development.md
@@ -42,6 +42,13 @@ version, optimized for browser apps.
## Test
+Install the CoffeeScript npm package globally, so you can type `cake` instead
+of `node_modules/coffee-script/bin/cake`.
+
+```bash
+npm install -g coffee-script
+```
+
First, you will need to obtain a couple of Dropbox tokens that will be used by
the automated tests.
@@ -51,12 +58,13 @@ cake tokens
Re-run the command above if the tests fail due to authentication errors.
-Once you have Dropbox tokens, you can run the test suite in node.js or in your
-default browser.
+Once you have Dropbox tokens, you can run the test suite in node.js, in your
+default browser, or as a Chrome application.
```bash
cake test
cake webtest
+cake chrometest
```
The library is automatically re-built when running tests, so you don't need to
@@ -68,11 +76,46 @@ The tests store all their data in folders named along the lines of
folders yourself.
-## Testing Chrome Extension
+### Solving Browser Issues
-The test suite opens up a couple of Dropbox authorization pages, and a page
-that cannot close itself. dropbox.js ships with a Google Chrome extension that
-can fully automate the testing process on Chrome.
+An easy method to test a browser in a virtual machine is to skip the automated
+browser opening.
+
+```bash
+BROWSER=false cake webtest
+```
+
+A similar method can be used to launch a specific browser.
+
+```bash
+BROWSER=firefox cake webtest
+```
+
+When fighting a bug, it can be useful to keep the server process running after
+the test suite completes, so tests can be re-started with a browser refresh.
+
+```bash
+BROWSER=false NO_EXIT=1 cake webtest
+```
+
+[Mocha's exclusive tests](http://visionmedia.github.com/mocha/#exclusive-tests)
+(`it.only` and `describe.only`) are very useful for quickly iterating while
+figuring out a bug.
+
+
+### Chrome Application / Extension Testing
+
+The tests for Chrome apps / extensions require manual intervention right now.
+
+The `cake chrometest` command will open a Google Chrome instance. The
+`dropbox.js Test Suite` application must be clicked.
+
+
+### Fully Automated Tests
+
+The test suite opens up the Dropbox authorization page a few times, and also
+pops up a page that cannot close itself. dropbox.js ships with a Google Chrome
+extension that can fully automate the testing process on Chrome / Chromium.
The extension is written in CoffeeScript, so you will have to compile it.
@@ -84,6 +127,69 @@ After compilation, have Chrome load the unpacked extension at
`test/chrome_extension` and click on the scary-looking toolbar icon to activate
the extension. The icon's color should turn red, to indicate that it is active.
-The extension performs some checks to prevent against attacks. However, for
-best results, you should disable the automation (by clicking on the extension
-icon) when you're not testing dropbox.js.
+The extension performs some checks to prevent against attacks. However, you
+should still disable the automation (by clicking on the extension icon) when
+you're not testing dropbox.js, just in case the extension code has bugs.
+
+
+## Release Process
+
+1. At the very least, test in node.js and in a browser before releasing.
+
+```bash
+cake test
+cake webtest
+```
+
+1. Bump the version in `package.json`.
+
+1. Publish a new npm package.
+
+```bash
+npm publish
+```
+
+1. Commit and tag the version bump on GitHub.
+
+```bash
+git add package.json
+git commit -m "Release X.Y.Z."
+git tag -a -m "Release X.Y.Z" vX.Y.Z
+git push
+git push --tags
+```
+
+1. If you haven't already, go to the
+ [cdnjs GitHub page](https://github.com/cdnjs/cdnjs) and fork it.
+
+1. If you haven't already, set up cdnjs on your machine.
+
+```bash
+cd ..
+git clone git@github.com:you/cdnjs.git
+cd cdnjs
+git remote add up git://github.com/cdnjs/cdnjs.git
+cd ../dropbox-js
+```
+
+1. Add the new release to your cdnjs fork.
+
+```bash
+cd ../cdnjs
+git checkout master
+git pull up master
+npm install
+git checkout -b dbXYZ
+mkdir ajax/libs/dropbox.js/X.Y.Z
+cp ../dropbox-js/lib/dropbox.min.js ajax/libs/dropbox.js/X.Y.Z/
+vim ajax/libs/dropbox.js/package.json # Replace "version"'s value with "X.Y.Z"
+npm test
+git add -A
+git commit -m "Added dropbox.js X.Y.Z"
+git push origin dbXYZ
+```
+
+1. Go to your cdnjs for on GitHub and open a pull request. Use these examples
+of accepted
+[major release pull request](https://github.com/cdnjs/cdnjs/pull/735) and
+[minor release pull request](https://github.com/cdnjs/cdnjs/pull/753).
diff --git a/lib/client/storage/dropbox/doc/getting_started.md b/lib/client/storage/dropbox/doc/getting_started.md
index 8fdea598..b58c0caa 100644
--- a/lib/client/storage/dropbox/doc/getting_started.md
+++ b/lib/client/storage/dropbox/doc/getting_started.md
@@ -12,7 +12,7 @@ This section describes how to get the library hooked up into your application.
To get started right away, place this snippet in your page's `
`.
```html
-
```
@@ -152,24 +152,20 @@ client.authenticate(function(error, client) {
## Error Handlng
-When Dropbox API calls fail, dropbox.js methods pass a `Dropbox.Error` instance
-as the first parameter in their callbacks. This parameter is named `error` in
-all the code snippets on this page.
+When Dropbox API calls fail, dropbox.js methods pass a `Dropbox.ApiError`
+instance as the first parameter in their callbacks. This parameter is named
+`error` in all the code snippets on this page.
If `error` is a truthy value, you should either recover from the error, or
notify the user that an error occurred. The `status` field in the
-`Dropbox.Error` instance contains the HTTP error code, which should be one of
-the
+`Dropbox.ApiError` instance contains the HTTP error code, which should be one
+of the
[error codes in the REST API](https://www.dropbox.com/developers/reference/api#error-handling).
The snippet below is a template for an extensive error handler.
```javascript
var showError = function(error) {
- if (window.console) { // Skip the "if" in node.js code.
- console.error(error);
- }
-
switch (error.status) {
case 401:
// If you're using dropbox.js, the only cause behind this error is that
@@ -202,6 +198,18 @@ var showError = function(error) {
};
```
+`Dropbox.Client` also supports a DOM event-like API for receiving all errors.
+This can be used to log API errors, or to upload them to your server for
+further analysis.
+
+```javascript
+client.onError.addListener(function(error) {
+ if (window.console) { // Skip the "if" in node.js code.
+ console.error(error);
+ }
+});
+```
+
## The Fun Part
diff --git a/lib/client/storage/dropbox/lib/dropbox.js b/lib/client/storage/dropbox/lib/dropbox.js
index baa1119c..05a073aa 100644
--- a/lib/client/storage/dropbox/lib/dropbox.js
+++ b/lib/client/storage/dropbox/lib/dropbox.js
@@ -1,8 +1,9 @@
// Generated by CoffeeScript 1.4.0
(function() {
- var Dropbox, DropboxClient, DropboxXhrArrayBufferView, DropboxXhrCanSendForms, DropboxXhrDoesPreflight, DropboxXhrIeMode, DropboxXhrRequest, add32, arrayToBase64, atob, atobNibble, base64Digits, base64HmacSha1, base64Sha1, btoa, btoaNibble, crypto, dropboxEncodeKey, hmacSha1, rotateLeft32, sha1, stringToArray, _base64Digits,
+ var Dropbox, DropboxChromeOnMessage, DropboxChromeSendMessage, DropboxClient, DropboxXhrArrayBufferView, DropboxXhrCanSendForms, DropboxXhrDoesPreflight, DropboxXhrIeMode, DropboxXhrRequest, DropboxXhrSendArrayBufferView, add32, arrayToBase64, atob, atobNibble, base64Digits, base64HmacSha1, base64Sha1, btoa, btoaNibble, crypto, dropboxEncodeKey, hmacSha1, rotateLeft32, sha1, stringToArray, _base64Digits,
__hasProp = {}.hasOwnProperty,
- __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
Dropbox = (function() {
@@ -32,9 +33,21 @@
this.url = url;
this.status = xhr.status;
if (xhr.responseType) {
- text = xhr.response || xhr.responseText;
+ try {
+ text = xhr.response || xhr.responseText;
+ } catch (e) {
+ try {
+ text = xhr.responseText;
+ } catch (e) {
+ text = null;
+ }
+ }
} else {
- text = xhr.responseText;
+ try {
+ text = xhr.responseText;
+ } catch (e) {
+ text = null;
+ }
}
if (text) {
try {
@@ -174,6 +187,11 @@
this.authServer = options.authServer || this.defaultAuthServer();
this.fileServer = options.fileServer || this.defaultFileServer();
this.downloadServer = options.downloadServer || this.defaultDownloadServer();
+ this.onXhr = new Dropbox.EventSource({
+ cancelable: true
+ });
+ this.onError = new Dropbox.EventSource;
+ this.onAuthStateChange = new Dropbox.EventSource;
this.oauth = new Dropbox.Oauth(options);
this.driver = null;
this.filter = null;
@@ -190,10 +208,11 @@
return this;
};
- Client.prototype.xhrFilter = function(filter) {
- this.filter = filter;
- return this;
- };
+ Client.prototype.onXhr = null;
+
+ Client.prototype.onError = null;
+
+ Client.prototype.onAuthStateChange = null;
Client.prototype.dropboxUid = function() {
return this.uid;
@@ -210,11 +229,17 @@
var oldAuthState, _fsmStep,
_this = this;
oldAuthState = null;
+ if (!(this.driver || this.authState === DropboxClient.DONE)) {
+ throw new Error("Call authDriver to set an authentication driver");
+ }
_fsmStep = function() {
var authUrl;
if (oldAuthState !== _this.authState) {
+ if (oldAuthState !== null) {
+ _this.onAuthStateChange.dispatch(_this);
+ }
oldAuthState = _this.authState;
- if (_this.driver.onAuthStateChange) {
+ if (_this.driver && _this.driver.onAuthStateChange) {
return _this.driver.onAuthStateChange(_this, _fsmStep);
}
}
@@ -256,7 +281,8 @@
});
case DropboxClient.DONE:
return callback(null, _this);
- case Dropbox.SIGNED_OFF:
+ case DropboxClient.SIGNED_OFF:
+ _this.authState = DropboxClient.RESET;
_this.reset();
return _fsmStep();
case DropboxClient.ERROR:
@@ -267,6 +293,10 @@
return this;
};
+ Client.prototype.isAuthenticated = function() {
+ return this.authState === DropboxClient.DONE;
+ };
+
Client.prototype.signOut = function(callback) {
var xhr,
_this = this;
@@ -276,8 +306,10 @@
if (error) {
return callback(error);
}
+ _this.authState = DropboxClient.RESET;
_this.reset();
_this.authState = DropboxClient.SIGNED_OFF;
+ _this.onAuthStateChange.dispatch(_this);
if (_this.driver.onAuthStateChange) {
return _this.driver.onAuthStateChange(_this, function() {
return callback(error);
@@ -292,17 +324,25 @@
return this.signOut(callback);
};
- Client.prototype.getUserInfo = function(callback) {
- var xhr;
+ Client.prototype.getUserInfo = function(options, callback) {
+ var httpCache, xhr;
+ if ((!callback) && (typeof options === 'function')) {
+ callback = options;
+ options = null;
+ }
+ httpCache = false;
+ if (options && options.httpCache) {
+ httpCache = true;
+ }
xhr = new Dropbox.Xhr('GET', this.urls.accountInfo);
- xhr.signWithOauth(this.oauth);
+ xhr.signWithOauth(this.oauth, httpCache);
return this.dispatchXhr(xhr, function(error, userData) {
return callback(error, Dropbox.UserInfo.parse(userData), userData);
});
};
Client.prototype.readFile = function(path, options, callback) {
- var params, rangeEnd, rangeHeader, rangeStart, responseType, xhr;
+ var httpCache, params, rangeEnd, rangeHeader, rangeStart, responseType, xhr;
if ((!callback) && (typeof options === 'function')) {
callback = options;
options = null;
@@ -310,6 +350,7 @@
params = {};
responseType = null;
rangeHeader = null;
+ httpCache = false;
if (options) {
if (options.versionTag) {
params.rev = options.versionTag;
@@ -335,9 +376,13 @@
} else if (options.start != null) {
rangeHeader = "bytes=" + options.start + "-";
}
+ if (options.httpCache) {
+ httpCache = true;
+ }
}
xhr = new Dropbox.Xhr('GET', "" + this.urls.getFile + "/" + (this.urlEncodePath(path)));
- xhr.setParams(params).signWithOauth(this.oauth).setResponseType(responseType);
+ xhr.setParams(params).signWithOauth(this.oauth, httpCache);
+ xhr.setResponseType(responseType);
if (rangeHeader) {
xhr.setHeader('Range', rangeHeader);
}
@@ -411,13 +456,65 @@
});
};
- Client.prototype.stat = function(path, options, callback) {
+ Client.prototype.resumableUploadStep = function(data, cursor, callback) {
+ var params, xhr;
+ if (cursor) {
+ params = {
+ offset: cursor.offset
+ };
+ if (cursor.tag) {
+ params.upload_id = cursor.tag;
+ }
+ } else {
+ params = {
+ offset: 0
+ };
+ }
+ xhr = new Dropbox.Xhr('POST', this.urls.chunkedUpload);
+ xhr.setBody(data).setParams(params).signWithOauth(this.oauth);
+ return this.dispatchXhr(xhr, function(error, cursor) {
+ if (error && error.status === 400 && error.response.upload_id && error.response.offset) {
+ return callback(null, Dropbox.UploadCursor.parse(error.response));
+ } else {
+ return callback(error, Dropbox.UploadCursor.parse(cursor));
+ }
+ });
+ };
+
+ Client.prototype.resumableUploadFinish = function(path, cursor, options, callback) {
var params, xhr;
if ((!callback) && (typeof options === 'function')) {
callback = options;
options = null;
}
+ params = {
+ upload_id: cursor.tag
+ };
+ if (options) {
+ if (options.lastVersionTag) {
+ params.parent_rev = options.lastVersionTag;
+ } else if (options.parentRev || options.parent_rev) {
+ params.parent_rev = options.parentRev || options.parent_rev;
+ }
+ if (options.noOverwrite) {
+ params.autorename = true;
+ }
+ }
+ xhr = new Dropbox.Xhr('POST', "" + this.urls.commitChunkedUpload + "/" + (this.urlEncodePath(path)));
+ xhr.setParams(params).signWithOauth(this.oauth);
+ return this.dispatchXhr(xhr, function(error, metadata) {
+ return callback(error, Dropbox.Stat.parse(metadata));
+ });
+ };
+
+ Client.prototype.stat = function(path, options, callback) {
+ var httpCache, params, xhr;
+ if ((!callback) && (typeof options === 'function')) {
+ callback = options;
+ options = null;
+ }
params = {};
+ httpCache = false;
if (options) {
if (options.version != null) {
params.rev = options.version;
@@ -434,11 +531,14 @@
if (options.cacheHash) {
params.hash = options.cacheHash;
}
+ if (options.httpCache) {
+ httpCache = true;
+ }
}
params.include_deleted || (params.include_deleted = 'false');
params.list || (params.list = 'false');
xhr = new Dropbox.Xhr('GET', "" + this.urls.metadata + "/" + (this.urlEncodePath(path)));
- xhr.setParams(params).signWithOauth(this.oauth);
+ xhr.setParams(params).signWithOauth(this.oauth, httpCache);
return this.dispatchXhr(xhr, function(error, metadata) {
var entries, entry, stat;
stat = Dropbox.Stat.parse(metadata);
@@ -476,6 +576,12 @@
if (options.versionTag) {
statOptions.versionTag = options.versionTag;
}
+ if (options.removed || options.deleted) {
+ statOptions.removed = options.removed || options.deleted;
+ }
+ if (options.httpCache) {
+ statOptions.httpCache = options.httpCache;
+ }
}
return this.stat(path, statOptions, function(error, stat, entry_stats) {
var entries, entry_stat;
@@ -536,17 +642,23 @@
};
Client.prototype.history = function(path, options, callback) {
- var params, xhr;
+ var httpCache, params, xhr;
if ((!callback) && (typeof options === 'function')) {
callback = options;
options = null;
}
params = {};
- if (options && (options.limit != null)) {
- params.rev_limit = options.limit;
+ httpCache = false;
+ if (options) {
+ if (options.limit != null) {
+ params.rev_limit = options.limit;
+ }
+ if (options.httpCache) {
+ httpCache = true;
+ }
}
xhr = new Dropbox.Xhr('GET', "" + this.urls.revisions + "/" + (this.urlEncodePath(path)));
- xhr.setParams(params).signWithOauth(this.oauth);
+ xhr.setParams(params).signWithOauth(this.oauth, httpCache);
return this.dispatchXhr(xhr, function(error, versions) {
var metadata, stats;
if (versions) {
@@ -628,7 +740,7 @@
};
Client.prototype.findByName = function(path, namePattern, options, callback) {
- var params, xhr;
+ var httpCache, params, xhr;
if ((!callback) && (typeof options === 'function')) {
callback = options;
options = null;
@@ -636,6 +748,7 @@
params = {
query: namePattern
};
+ httpCache = false;
if (options) {
if (options.limit != null) {
params.file_limit = options.limit;
@@ -643,9 +756,12 @@
if (options.removed || options.deleted) {
params.include_deleted = true;
}
+ if (options.httpCache) {
+ httpCache = true;
+ }
}
xhr = new Dropbox.Xhr('GET', "" + this.urls.search + "/" + (this.urlEncodePath(path)));
- xhr.setParams(params).signWithOauth(this.oauth);
+ xhr.setParams(params).signWithOauth(this.oauth, httpCache);
return this.dispatchXhr(xhr, function(error, results) {
var metadata, stats;
if (results) {
@@ -784,15 +900,22 @@
};
Client.prototype.reset = function() {
+ var oldAuthState;
this.uid = null;
this.oauth.setToken(null, '');
+ oldAuthState = this.authState;
this.authState = DropboxClient.RESET;
+ if (oldAuthState !== this.authState) {
+ this.onAuthStateChange.dispatch(this);
+ }
this.authError = null;
this._credentials = null;
return this;
};
Client.prototype.setCredentials = function(credentials) {
+ var oldAuthState;
+ oldAuthState = this.authState;
this.oauth.reset(credentials);
this.uid = credentials.uid || null;
if (credentials.authState) {
@@ -806,6 +929,9 @@
}
this.authError = null;
this._credentials = null;
+ if (oldAuthState !== this.authState) {
+ this.onAuthStateChange.dispatch(this);
+ }
return this;
};
@@ -833,6 +959,8 @@
media: "" + this.apiServer + "/1/media/" + this.fileRoot,
copyRef: "" + this.apiServer + "/1/copy_ref/" + this.fileRoot,
thumbnails: "" + this.fileServer + "/1/thumbnails/" + this.fileRoot,
+ chunkedUpload: "" + this.fileServer + "/1/chunked_upload",
+ commitChunkedUpload: "" + this.fileServer + "/1/commit_chunked_upload/" + this.fileRoot,
fileopsCopy: "" + this.apiServer + "/1/fileops/copy",
fileopsCreateFolder: "" + this.apiServer + "/1/fileops/create_folder",
fileopsDelete: "" + this.apiServer + "/1/fileops/delete",
@@ -840,6 +968,8 @@
};
};
+ Client.prototype.authState = null;
+
Client.ERROR = 0;
Client.RESET = 1;
@@ -893,14 +1023,12 @@
Client.prototype.dispatchXhr = function(xhr, callback) {
var nativeXhr;
xhr.setCallback(callback);
+ xhr.onError = this.onError;
xhr.prepare();
nativeXhr = xhr.xhr;
- if (this.filter) {
- if (!this.filter(nativeXhr, xhr)) {
- return nativeXhr;
- }
+ if (this.onXhr.dispatch(xhr)) {
+ xhr.send();
}
- xhr.send();
return nativeXhr;
};
@@ -987,6 +1115,7 @@
function BrowserBase(options) {
this.rememberUser = (options != null ? options.rememberUser : void 0) || false;
this.scope = (options != null ? options.scope : void 0) || 'default';
+ this.storageKey = null;
}
BrowserBase.prototype.onAuthStateChange = function(client, callback) {
@@ -1115,9 +1244,7 @@
};
Redirect.prototype.doAuthorize = function(authUrl) {
- /* */
- return DOM.openWindow(authUrl);
- /* return window.location.assign(authUrl); */
+ return window.location.assign(authUrl);
};
Redirect.prototype.computeUrl = function() {
@@ -1188,7 +1315,7 @@
if (client.authState === DropboxClient.RESET) {
return this.loadCredentials(function(credentials) {
if (credentials && credentials.authState) {
- return this.forgetCredentials(superCall);
+ return _this.forgetCredentials(superCall);
}
return superCall();
});
@@ -1263,10 +1390,241 @@
return window.addEventListener('message', listener, false);
};
+ Popup.oauthReceiver = function() {
+ return window.addEventListener('load', function() {
+ var opener;
+ opener = window.opener;
+ if (window.parent !== window.top) {
+ opener || (opener = window.parent);
+ }
+ if (opener) {
+ try {
+ opener.postMessage(window.location.href, '*');
+ } catch (e) {
+
+ }
+ return window.close();
+ }
+ });
+ };
+
return Popup;
})(Dropbox.Drivers.BrowserBase);
+ DropboxChromeOnMessage = null;
+
+ DropboxChromeSendMessage = null;
+
+ if (typeof chrome !== "undefined" && chrome !== null) {
+ if (chrome.runtime) {
+ if (chrome.runtime.onMessage) {
+ DropboxChromeOnMessage = chrome.runtime.onMessage;
+ }
+ if (chrome.runtime.sendMessage) {
+ DropboxChromeSendMessage = function(m) {
+ return chrome.runtime.sendMessage(m);
+ };
+ }
+ }
+ if (chrome.extension) {
+ if (chrome.extension.onMessage) {
+ DropboxChromeOnMessage || (DropboxChromeOnMessage = chrome.extension.onMessage);
+ }
+ if (chrome.extension.sendMessage) {
+ DropboxChromeSendMessage || (DropboxChromeSendMessage = function(m) {
+ return chrome.extension.sendMessage(m);
+ });
+ }
+ }
+ if (!DropboxChromeOnMessage) {
+ (function() {
+ var page, pageHack;
+ pageHack = function(page) {
+ if (page.Dropbox) {
+ Dropbox.Drivers.Chrome.prototype.onMessage = page.Dropbox.Drivers.Chrome.onMessage;
+ return Dropbox.Drivers.Chrome.prototype.sendMessage = page.Dropbox.Drivers.Chrome.sendMessage;
+ } else {
+ page.Dropbox = Dropbox;
+ Dropbox.Drivers.Chrome.prototype.onMessage = new Dropbox.EventSource;
+ return Dropbox.Drivers.Chrome.prototype.sendMessage = function(m) {
+ return Dropbox.Drivers.Chrome.prototype.onMessage.dispatch(m);
+ };
+ }
+ };
+ if (chrome.extension && chrome.extension.getBackgroundPage) {
+ if (page = chrome.extension.getBackgroundPage()) {
+ return pageHack(page);
+ }
+ }
+ if (chrome.runtime && chrome.runtime.getBackgroundPage) {
+ return chrome.runtime.getBackgroundPage(function(page) {
+ return pageHack(page);
+ });
+ }
+ })();
+ }
+ }
+
+ Dropbox.Drivers.Chrome = (function() {
+
+ Chrome.prototype.onMessage = DropboxChromeOnMessage;
+
+ Chrome.prototype.sendMessage = DropboxChromeSendMessage;
+
+ Chrome.prototype.expandUrl = function(url) {
+ if (chrome.runtime && chrome.runtime.getURL) {
+ return chrome.runtime.getURL(url);
+ }
+ if (chrome.extension && chrome.extension.getURL) {
+ return chrome.extension.getURL(url);
+ }
+ return url;
+ };
+
+ function Chrome(options) {
+ var receiverPath, scope;
+ receiverPath = (options && options.receiverPath) || 'chrome_oauth_receiver.html';
+ this.receiverUrl = this.expandUrl(receiverPath);
+ this.tokenRe = new RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)");
+ scope = (options && options.scope) || 'default';
+ this.storageKey = "dropbox_js_" + scope + "_credentials";
+ }
+
+ Chrome.prototype.onAuthStateChange = function(client, callback) {
+ var _this = this;
+ switch (client.authState) {
+ case Dropbox.Client.RESET:
+ return this.loadCredentials(function(credentials) {
+ if (credentials) {
+ if (credentials.authState) {
+ return _this.forgetCredentials(callback);
+ }
+ client.setCredentials(credentials);
+ }
+ return callback();
+ });
+ case Dropbox.Client.DONE:
+ return this.storeCredentials(client.credentials(), callback);
+ case Dropbox.Client.SIGNED_OFF:
+ return this.forgetCredentials(callback);
+ case Dropbox.Client.ERROR:
+ return this.forgetCredentials(callback);
+ default:
+ return callback();
+ }
+ };
+
+ Chrome.prototype.doAuthorize = function(authUrl, token, tokenSecret, callback) {
+ var window;
+ window = {
+ handle: null
+ };
+ this.listenForMessage(token, window, callback);
+ return this.openWindow(authUrl, function(handle) {
+ return window.handle = handle;
+ });
+ };
+
+ Chrome.prototype.openWindow = function(url, callback) {
+ if (chrome.tabs && chrome.tabs.create) {
+ chrome.tabs.create({
+ url: url,
+ active: true,
+ pinned: false
+ }, function(tab) {
+ return callback(tab);
+ });
+ return this;
+ }
+ if (chrome.app && chrome.app.window && chrome.app.window.create) {
+ chrome.app.window.create(url, {
+ frame: 'none',
+ id: 'dropbox-auth'
+ }, function(window) {
+ return callback(window);
+ });
+ return this;
+ }
+ return this;
+ };
+
+ Chrome.prototype.closeWindow = function(handle) {
+ if (chrome.tabs && chrome.tabs.remove && handle.id) {
+ chrome.tabs.remove(handle.id);
+ return this;
+ }
+ if (chrome.app && chrome.app.window && handle.close) {
+ handle.close();
+ return this;
+ }
+ return this;
+ };
+
+ Chrome.prototype.url = function() {
+ return this.receiverUrl;
+ };
+
+ Chrome.prototype.listenForMessage = function(token, window, callback) {
+ var listener,
+ _this = this;
+ listener = function(message, sender) {
+ var match;
+ if (sender && sender.tab) {
+ if (sender.tab.url.substring(0, _this.receiverUrl.length) !== _this.receiverUrl) {
+ return;
+ }
+ }
+ match = _this.tokenRe.exec(message.dropbox_oauth_receiver_href || '');
+ if (match && decodeURIComponent(match[2]) === token) {
+ if (window.handle) {
+ _this.closeWindow(window.handle);
+ }
+ _this.onMessage.removeListener(listener);
+ return callback();
+ }
+ };
+ return this.onMessage.addListener(listener);
+ };
+
+ Chrome.prototype.storeCredentials = function(credentials, callback) {
+ var items;
+ items = {};
+ items[this.storageKey] = credentials;
+ chrome.storage.local.set(items, callback);
+ return this;
+ };
+
+ Chrome.prototype.loadCredentials = function(callback) {
+ var _this = this;
+ chrome.storage.local.get(this.storageKey, function(items) {
+ return callback(items[_this.storageKey] || null);
+ });
+ return this;
+ };
+
+ Chrome.prototype.forgetCredentials = function(callback) {
+ chrome.storage.local.remove(this.storageKey, callback);
+ return this;
+ };
+
+ Chrome.oauthReceiver = function() {
+ return window.addEventListener('load', function() {
+ var driver;
+ driver = new Dropbox.Drivers.Chrome();
+ driver.sendMessage({
+ dropbox_oauth_receiver_href: window.location.href
+ });
+ if (window.close) {
+ return window.close();
+ }
+ });
+ };
+
+ return Chrome;
+
+ })();
+
Dropbox.Drivers.NodeServer = (function() {
function NodeServer(options) {
@@ -1361,6 +1719,60 @@
})();
+ Dropbox.EventSource = (function() {
+
+ function EventSource(options) {
+ this._cancelable = options && options.cancelable;
+ this._listeners = [];
+ }
+
+ EventSource.prototype.addListener = function(listener) {
+ if (typeof listener !== 'function') {
+ throw new TypeError('Invalid listener type; expected function');
+ }
+ if (__indexOf.call(this._listeners, listener) < 0) {
+ this._listeners.push(listener);
+ }
+ return this;
+ };
+
+ EventSource.prototype.removeListener = function(listener) {
+ var i, index, subscriber, _i, _len, _ref;
+ if (this._listeners.indexOf) {
+ index = this._listeners.indexOf(listener);
+ if (index !== -1) {
+ this._listeners.splice(index, 1);
+ }
+ } else {
+ _ref = this._listeners;
+ for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
+ subscriber = _ref[i];
+ if (subscriber === listener) {
+ this._listeners.splice(i, 1);
+ break;
+ }
+ }
+ }
+ return this;
+ };
+
+ EventSource.prototype.dispatch = function(event) {
+ var listener, returnValue, _i, _len, _ref;
+ _ref = this._listeners;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ listener = _ref[_i];
+ returnValue = listener(event);
+ if (this._cancelable && returnValue === false) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ return EventSource;
+
+ })();
+
base64HmacSha1 = function(string, key) {
return arrayToBase64(hmacSha1(stringToArray(string), stringToArray(key), string.length, key.length));
};
@@ -1720,6 +2132,10 @@
PulledChanges.prototype.shouldBackOff = void 0;
+ PulledChanges.prototype.cursor = function() {
+ return this.cursorTag;
+ };
+
function PulledChanges(deltaInfo) {
var entry;
this.blankSlate = deltaInfo.reset || false;
@@ -1787,13 +2203,21 @@
}
};
- PublicUrl.prototype.url = void 0;
+ PublicUrl.prototype.url = null;
- PublicUrl.prototype.expiresAt = void 0;
+ PublicUrl.prototype.expiresAt = null;
- PublicUrl.prototype.isDirect = void 0;
+ PublicUrl.prototype.isDirect = null;
- PublicUrl.prototype.isPreview = void 0;
+ PublicUrl.prototype.isPreview = null;
+
+ PublicUrl.prototype.json = function() {
+ return this._json || (this._json = {
+ url: this.url,
+ expires: this.expiresAt.toString(),
+ direct: this.isDirect
+ });
+ };
function PublicUrl(urlData, isDirect) {
this.url = urlData.url;
@@ -1803,9 +2227,14 @@
} else if (isDirect === false) {
this.isDirect = false;
} else {
- this.isDirect = Date.now() - this.expiresAt <= 86400000;
+ if ('direct' in urlData) {
+ this.isDirect = urlData.direct;
+ } else {
+ this.isDirect = Date.now() - this.expiresAt <= 86400000;
+ }
}
this.isPreview = !this.isDirect;
+ this._json = null;
}
return PublicUrl;
@@ -1822,17 +2251,26 @@
}
};
- CopyReference.prototype.tag = void 0;
+ CopyReference.prototype.tag = null;
- CopyReference.prototype.expiresAt = void 0;
+ CopyReference.prototype.expiresAt = null;
+
+ CopyReference.prototype.json = function() {
+ return this._json || (this._json = {
+ copy_ref: this.tag,
+ expires: this.expiresAt.toString()
+ });
+ };
function CopyReference(refData) {
if (typeof refData === 'object') {
this.tag = refData.copy_ref;
this.expiresAt = new Date(Date.parse(refData.expires));
+ this._json = refData;
} else {
this.tag = refData;
- this.expiresAt = new Date();
+ this.expiresAt = new Date(Math.ceil(Date.now() / 1000) * 1000);
+ this._json = null;
}
}
@@ -1878,8 +2316,13 @@
Stat.prototype.clientModifiedAt = null;
+ Stat.prototype.json = function() {
+ return this._json;
+ };
+
function Stat(metadata) {
var lastIndex, nameSlash, _ref, _ref1;
+ this._json = metadata;
this.path = metadata.path;
if (this.path.substring(0, 1) !== '/') {
this.path = '/' + this.path;
@@ -1930,6 +2373,53 @@
})();
+ Dropbox.UploadCursor = (function() {
+
+ UploadCursor.parse = function(cursorData) {
+ if (cursorData && (typeof cursorData === 'object' || typeof cursorData === 'string')) {
+ return new Dropbox.UploadCursor(cursorData);
+ } else {
+ return cursorData;
+ }
+ };
+
+ UploadCursor.prototype.tag = null;
+
+ UploadCursor.prototype.offset = null;
+
+ UploadCursor.prototype.expiresAt = null;
+
+ UploadCursor.prototype.json = function() {
+ return this._json || (this._json = {
+ upload_id: this.tag,
+ offset: this.offset,
+ expires: this.expiresAt.toString()
+ });
+ };
+
+ function UploadCursor(cursorData) {
+ this.replace(cursorData);
+ }
+
+ UploadCursor.prototype.replace = function(cursorData) {
+ if (typeof cursorData === 'object') {
+ this.tag = cursorData.upload_id || null;
+ this.offset = cursorData.offset || 0;
+ this.expiresAt = new Date(Date.parse(cursorData.expires) || Date.now());
+ this._json = cursorData;
+ } else {
+ this.tag = cursorData || null;
+ this.offset = 0;
+ this.expiresAt = new Date(Math.floor(Date.now() / 1000) * 1000);
+ this._json = null;
+ }
+ return this;
+ };
+
+ return UploadCursor;
+
+ })();
+
Dropbox.UserInfo = (function() {
UserInfo.parse = function(userInfo) {
@@ -1960,8 +2450,13 @@
UserInfo.prototype.sharedBytes = null;
+ UserInfo.prototype.json = function() {
+ return this._json;
+ };
+
function UserInfo(userInfo) {
var lastIndex;
+ this._json = userInfo;
this.name = userInfo.display_name;
this.email = userInfo.email;
this.countryCode = userInfo.country || null;
@@ -2006,15 +2501,21 @@
if (typeof Uint8Array === 'undefined') {
DropboxXhrArrayBufferView = null;
+ DropboxXhrSendArrayBufferView = false;
} else {
- DropboxXhrArrayBufferView = (new Uint8Array(0)).__proto__.__proto__.constructor;
+ if (Object.getPrototypeOf) {
+ DropboxXhrArrayBufferView = Object.getPrototypeOf(Object.getPrototypeOf(new Uint8Array(0))).constructor;
+ } else if (Object.__proto__) {
+ DropboxXhrArrayBufferView = (new Uint8Array(0)).__proto__.__proto__.constructor;
+ }
+ DropboxXhrSendArrayBufferView = DropboxXhrArrayBufferView !== Object;
}
Dropbox.Xhr = (function() {
Xhr.Request = DropboxXhrRequest;
- Xhr.ieMode = DropboxXhrIeMode;
+ Xhr.ieXdr = DropboxXhrIeMode;
Xhr.canSendForms = DropboxXhrCanSendForms;
@@ -2022,6 +2523,8 @@
Xhr.ArrayBufferView = DropboxXhrArrayBufferView;
+ Xhr.sendArrayBufferView = DropboxXhrSendArrayBufferView;
+
function Xhr(method, baseUrl) {
this.method = method;
this.isGet = this.method === 'GET';
@@ -2034,8 +2537,13 @@
this.responseType = null;
this.callback = null;
this.xhr = null;
+ this.onError = null;
}
+ Xhr.prototype.xhr = null;
+
+ Xhr.prototype.onError = null;
+
Xhr.prototype.setParams = function(params) {
if (this.signed) {
throw new Error('setParams called after addOauthParams or addOauthHeader');
@@ -2052,11 +2560,17 @@
return this;
};
- Xhr.prototype.signWithOauth = function(oauth) {
- if (Dropbox.Xhr.ieMode || (Dropbox.Xhr.doesPreflight && (!this.preflight))) {
+ Xhr.prototype.signWithOauth = function(oauth, cacheFriendly) {
+ if (Dropbox.Xhr.ieXdr) {
return this.addOauthParams(oauth);
- } else {
+ } else if (this.preflight || !Dropbox.Xhr.doesPreflight) {
return this.addOauthHeader(oauth);
+ } else {
+ if (this.isGet && cacheFriendly) {
+ return this.addOauthHeader(oauth);
+ } else {
+ return this.addOauthParams(oauth);
+ }
}
};
@@ -2086,10 +2600,13 @@
if (this.body !== null) {
throw new Error('Request already has a body');
}
- if (!this.preflight) {
- if (!((typeof FormData !== 'undefined') && (body instanceof FormData))) {
- this.preflight = true;
- }
+ if (typeof body === 'string') {
+
+ } else if ((typeof FormData !== 'undefined') && (body instanceof FormData)) {
+
+ } else {
+ this.headers['Content-Type'] = 'application/octet-stream';
+ this.preflight = true;
}
this.body = body;
return this;
@@ -2120,18 +2637,24 @@
throw new Error('Request already has a body');
}
if (this.isGet) {
- throw new Error('paramsToBody cannot be called on GET requests');
+ throw new Error('setFileField cannot be called on GET requests');
}
if (typeof fileData === 'object' && typeof Blob !== 'undefined') {
- if ((typeof ArrayBuffer !== "undefined" && ArrayBuffer !== null) && fileData instanceof ArrayBuffer) {
- fileData = new Uint8Array(fileData);
- }
- if (Dropbox.Xhr.ArrayBufferView && fileData instanceof Dropbox.Xhr.ArrayBufferView) {
- contentType || (contentType = 'application/octet-stream');
- fileData = new Blob([fileData], {
- type: contentType
- });
+ if (typeof ArrayBuffer !== 'undefined') {
+ if (fileData instanceof ArrayBuffer) {
+ if (Dropbox.Xhr.sendArrayBufferView) {
+ fileData = new Uint8Array(fileData);
+ }
+ } else {
+ if (!Dropbox.Xhr.sendArrayBufferView && fileData.byteOffset === 0 && fileData.buffer instanceof ArrayBuffer) {
+ fileData = fileData.buffer;
+ }
+ }
}
+ contentType || (contentType = 'application/octet-stream');
+ fileData = new Blob([fileData], {
+ type: contentType
+ });
if (typeof File !== 'undefined' && fileData instanceof File) {
fileData = new Blob([fileData], {
type: fileData.type
@@ -2184,10 +2707,10 @@
};
Xhr.prototype.prepare = function() {
- var header, ieMode, value, _ref,
+ var header, ieXdr, value, _ref,
_this = this;
- ieMode = Dropbox.Xhr.ieMode;
- if (this.isGet || this.body !== null || ieMode) {
+ ieXdr = Dropbox.Xhr.ieXdr;
+ if (this.isGet || this.body !== null || ieXdr) {
this.paramsToUrl();
if (this.body !== null && typeof this.body === 'string') {
this.headers['Content-Type'] = 'text/plain; charset=utf8';
@@ -2196,20 +2719,24 @@
this.paramsToBody();
}
this.xhr = new Dropbox.Xhr.Request();
- if (ieMode) {
+ if (ieXdr) {
this.xhr.onload = function() {
- return _this.onLoad();
+ return _this.onXdrLoad();
};
this.xhr.onerror = function() {
- return _this.onError();
+ return _this.onXdrError();
};
+ this.xhr.ontimeout = function() {
+ return _this.onXdrError();
+ };
+ this.xhr.onprogress = function() {};
} else {
this.xhr.onreadystatechange = function() {
return _this.onReadyStateChange();
};
}
this.xhr.open(this.method, this.url, true);
- if (!ieMode) {
+ if (!ieXdr) {
_ref = this.headers;
for (header in _ref) {
if (!__hasProp.call(_ref, header)) continue;
@@ -2234,13 +2761,13 @@
this.callback = callback || this.callback;
if (this.body !== null) {
body = this.body;
- if (Dropbox.Xhr.ArrayBufferView && body instanceof ArrayBuffer) {
+ if (Dropbox.Xhr.sendArrayBufferView && body instanceof ArrayBuffer) {
body = new Uint8Array(body);
}
try {
this.xhr.send(body);
} catch (e) {
- if (typeof Blob !== 'undefined' && Dropbox.Xhr.ArrayBufferView && body instanceof Dropbox.Xhr.ArrayBufferView) {
+ if (!Dropbox.Xhr.sendArrayBufferView && typeof Blob !== 'undefined') {
body = new Blob([body], {
type: 'application/octet-stream'
});
@@ -2288,6 +2815,9 @@
}
if (this.xhr.status < 200 || this.xhr.status >= 300) {
apiError = new Dropbox.ApiError(this.xhr, this.method, this.url);
+ if (this.onError) {
+ this.onError.dispatch(apiError);
+ }
this.callback(apiError);
return true;
}
@@ -2343,7 +2873,7 @@
return true;
};
- Xhr.prototype.onLoad = function() {
+ Xhr.prototype.onXdrLoad = function() {
var text;
text = this.xhr.responseText;
switch (this.xhr.contentType) {
@@ -2360,9 +2890,12 @@
return true;
};
- Xhr.prototype.onError = function() {
+ Xhr.prototype.onXdrError = function() {
var apiError;
apiError = new Dropbox.ApiError(this.xhr, this.method, this.url);
+ if (this.onError) {
+ this.onError.dispatch(apiError);
+ }
this.callback(apiError);
return true;
};
diff --git a/lib/client/storage/dropbox/lib/dropbox.min.js b/lib/client/storage/dropbox/lib/dropbox.min.js
index bb51c44f..f14c0b0b 100644
--- a/lib/client/storage/dropbox/lib/dropbox.min.js
+++ b/lib/client/storage/dropbox/lib/dropbox.min.js
@@ -1,2 +1,2 @@
-(function(){var t,e,r,n,o,i,s,a,h,u,l,p,c,d,f,y,v,m,g,w,S,b,T,_={}.hasOwnProperty,E=function(t,e){function r(){this.constructor=t}for(var n in e)_.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t};if(t=function(){function t(t){this.client=new e(t)}return t}(),t.ApiError=function(){function t(t,e,r){var n;if(this.method=e,this.url=r,this.status=t.status,n=t.responseType?t.response||t.responseText:t.responseText)try{this.responseText=""+n,this.response=JSON.parse(n)}catch(o){this.response=null}else this.responseText="(no response)",this.response=null}return t.prototype.status=void 0,t.prototype.method=void 0,t.prototype.url=void 0,t.prototype.responseText=void 0,t.prototype.response=void 0,t.prototype.toString=function(){return"Dropbox API error "+this.status+" from "+this.method+" "+this.url+" :: "+this.responseText},t.prototype.inspect=function(){return""+this},t}(),"undefined"!=typeof window&&null!==window?window.atob&&window.btoa?(u=function(t){return window.atob(t)},f=function(t){return window.btoa(t)}):(p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",y=function(t,e,r){var n,o;for(o=3-e,t<<=8*o,n=3;n>=o;)r.push(p.charAt(63&t>>6*n)),n-=1;for(n=e;3>n;)r.push("="),n+=1;return null},l=function(t,e,r){var n,o;for(o=4-e,t<<=6*o,n=2;n>=o;)r.push(String.fromCharCode(255&t>>8*n)),n-=1;return null},f=function(t){var e,r,n,o,i,s;for(o=[],e=0,r=0,n=i=0,s=t.length;s>=0?s>i:i>s;n=s>=0?++i:--i)e=e<<8|t.charCodeAt(n),r+=1,3===r&&(y(e,r,o),e=r=0);return r>0&&y(e,r,o),o.join("")},u=function(t){var e,r,n,o,i,s,a;for(i=[],e=0,n=0,o=s=0,a=t.length;(a>=0?a>s:s>a)&&(r=t.charAt(o),"="!==r);o=a>=0?++s:--s)e=e<<6|p.indexOf(r),n+=1,4===n&&(l(e,n,i),e=n=0);return n>0&&l(e,n,i),i.join("")}):(u=function(t){var e,r;return e=new Buffer(t,"base64"),function(){var t,n,o;for(o=[],r=t=0,n=e.length;n>=0?n>t:t>n;r=n>=0?++t:--t)o.push(String.fromCharCode(e[r]));return o}().join("")},f=function(t){var e,r;return e=new Buffer(function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}()),e.toString("base64")}),t.Client=function(){function r(e){this.sandbox=e.sandbox||!1,this.apiServer=e.server||this.defaultApiServer(),this.authServer=e.authServer||this.defaultAuthServer(),this.fileServer=e.fileServer||this.defaultFileServer(),this.downloadServer=e.downloadServer||this.defaultDownloadServer(),this.oauth=new t.Oauth(e),this.driver=null,this.filter=null,this.uid=null,this.authState=null,this.authError=null,this._credentials=null,this.setCredentials(e),this.setupUrls()}return r.prototype.authDriver=function(t){return this.driver=t,this},r.prototype.xhrFilter=function(t){return this.filter=t,this},r.prototype.dropboxUid=function(){return this.uid},r.prototype.credentials=function(){return this._credentials||this.computeCredentials(),this._credentials},r.prototype.authenticate=function(r){var n,o,i=this;return n=null,o=function(){var s;if(n!==i.authState&&(n=i.authState,i.driver.onAuthStateChange))return i.driver.onAuthStateChange(i,o);switch(i.authState){case e.RESET:return i.requestToken(function(t,r){var n,s;return t?(i.authError=t,i.authState=e.ERROR):(n=r.oauth_token,s=r.oauth_token_secret,i.oauth.setToken(n,s),i.authState=e.REQUEST),i._credentials=null,o()});case e.REQUEST:return s=i.authorizeUrl(i.oauth.token),i.driver.doAuthorize(s,i.oauth.token,i.oauth.tokenSecret,function(){return i.authState=e.AUTHORIZED,i._credentials=null,o()});case e.AUTHORIZED:return i.getAccessToken(function(t,r){return t?(i.authError=t,i.authState=e.ERROR):(i.oauth.setToken(r.oauth_token,r.oauth_token_secret),i.uid=r.uid,i.authState=e.DONE),i._credentials=null,o()});case e.DONE:return r(null,i);case t.SIGNED_OFF:return i.reset(),o();case e.ERROR:return r(i.authError)}},o(),this},r.prototype.signOut=function(r){var n,o=this;return n=new t.Xhr("POST",this.urls.signOut),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(t){return t?r(t):(o.reset(),o.authState=e.SIGNED_OFF,o.driver.onAuthStateChange?o.driver.onAuthStateChange(o,function(){return r(t)}):r(t))})},r.prototype.signOff=function(t){return this.signOut(t)},r.prototype.getUserInfo=function(e){var r;return r=new t.Xhr("GET",this.urls.accountInfo),r.signWithOauth(this.oauth),this.dispatchXhr(r,function(r,n){return e(r,t.UserInfo.parse(n),n)})},r.prototype.readFile=function(e,r,n){var o,i,s,a,h,u;return n||"function"!=typeof r||(n=r,r=null),o={},h=null,s=null,r&&(r.versionTag?o.rev=r.versionTag:r.rev&&(o.rev=r.rev),r.arrayBuffer?h="arraybuffer":r.blob?h="blob":r.binary&&(h="b"),r.length?(null!=r.start?(a=r.start,i=r.start+r.length-1):(a="",i=r.length),s="bytes="+a+"-"+i):null!=r.start&&(s="bytes="+r.start+"-")),u=new t.Xhr("GET",""+this.urls.getFile+"/"+this.urlEncodePath(e)),u.setParams(o).signWithOauth(this.oauth).setResponseType(h),s&&u.setHeader("Range",s),this.dispatchXhr(u,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.writeFile=function(e,r,n,o){var i;return o||"function"!=typeof n||(o=n,n=null),i=t.Xhr.canSendForms&&"object"==typeof r,i?this.writeFileUsingForm(e,r,n,o):this.writeFileUsingPut(e,r,n,o)},r.prototype.writeFileUsingForm=function(e,r,n,o){var i,s,a,h;return a=e.lastIndexOf("/"),-1===a?(i=e,e=""):(i=e.substring(a),e=e.substring(0,a)),s={file:i},n&&(n.noOverwrite&&(s.overwrite="false"),n.lastVersionTag?s.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(s.parent_rev=n.parentRev||n.parent_rev)),h=new t.Xhr("POST",""+this.urls.postFile+"/"+this.urlEncodePath(e)),h.setParams(s).signWithOauth(this.oauth).setFileField("file",i,r,"application/octet-stream"),delete s.file,this.dispatchXhr(h,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.writeFileUsingPut=function(e,r,n,o){var i,s;return i={},n&&(n.noOverwrite&&(i.overwrite="false"),n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev)),s=new t.Xhr("POST",""+this.urls.putFile+"/"+this.urlEncodePath(e)),s.setBody(r).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.stat=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&(null!=r.version&&(o.rev=r.version),(r.removed||r.deleted)&&(o.include_deleted="true"),r.readDir&&(o.list="true",r.readDir!==!0&&(o.file_limit=""+r.readDir)),r.cacheHash&&(o.hash=r.cacheHash)),o.include_deleted||(o.include_deleted="false"),o.list||(o.list="false"),i=new t.Xhr("GET",""+this.urls.metadata+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i,s;return s=t.Stat.parse(r),o=(null!=r?r.contents:void 0)?function(){var e,n,o,s;for(o=r.contents,s=[],e=0,n=o.length;n>e;e++)i=o[e],s.push(t.Stat.parse(i));return s}():void 0,n(e,s,o)})},r.prototype.readdir=function(t,e,r){var n;return r||"function"!=typeof e||(r=e,e=null),n={readDir:!0},e&&(null!=e.limit&&(n.readDir=e.limit),e.versionTag&&(n.versionTag=e.versionTag)),this.stat(t,n,function(t,e,n){var o,i;return o=n?function(){var t,e,r;for(r=[],t=0,e=n.length;e>t;t++)i=n[t],r.push(i.name);return r}():null,r(t,o,e,n)})},r.prototype.metadata=function(t,e,r){return this.stat(t,e,r)},r.prototype.makeUrl=function(e,r,n){var o,i,s,a,h;return n||"function"!=typeof r||(n=r,r=null),i=r&&(r["long"]||r.longUrl||r.downloadHack)?{short_url:"false"}:{},e=this.urlEncodePath(e),s=""+this.urls.shares+"/"+e,o=!1,a=!1,r&&(r.downloadHack?(o=!0,a=!0):r.download&&(o=!0,s=""+this.urls.media+"/"+e)),h=new t.Xhr("POST",s).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(h,function(e,r){return a&&r&&r.url&&(r.url=r.url.replace(this.authServer,this.downloadServer)),n(e,t.PublicUrl.parse(r,o))})},r.prototype.history=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&null!=r.limit&&(o.rev_limit=r.limit),i=new t.Xhr("GET",""+this.urls.revisions+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i;return i=r?function(){var e,n,i;for(i=[],e=0,n=r.length;n>e;e++)o=r[e],i.push(t.Stat.parse(o));return i}():void 0,n(e,i)})},r.prototype.revisions=function(t,e,r){return this.history(t,e,r)},r.prototype.thumbnailUrl=function(t,e){var r;return r=this.thumbnailXhr(t,e),r.paramsToUrl().url},r.prototype.readThumbnail=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o="b",r&&r.blob&&(o="blob"),i=this.thumbnailXhr(e,r),i.setResponseType(o),this.dispatchXhr(i,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.thumbnailXhr=function(e,r){var n,o;return n={},r&&(r.format?n.format=r.format:r.png&&(n.format="png"),r.size&&(n.size=r.size)),o=new t.Xhr("GET",""+this.urls.thumbnails+"/"+this.urlEncodePath(e)),o.setParams(n).signWithOauth(this.oauth)},r.prototype.revertFile=function(e,r,n){var o;return o=new t.Xhr("POST",""+this.urls.restore+"/"+this.urlEncodePath(e)),o.setParams({rev:r}).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.restore=function(t,e,r){return this.revertFile(t,e,r)},r.prototype.findByName=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={query:r},n&&(null!=n.limit&&(i.file_limit=n.limit),(n.removed||n.deleted)&&(i.include_deleted=!0)),s=new t.Xhr("GET",""+this.urls.search+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){var n,i;return i=r?function(){var e,o,i;for(i=[],e=0,o=r.length;o>e;e++)n=r[e],i.push(t.Stat.parse(n));return i}():void 0,o(e,i)})},r.prototype.search=function(t,e,r,n){return this.findByName(t,e,r,n)},r.prototype.makeCopyReference=function(e,r){var n;return n=new t.Xhr("GET",""+this.urls.copyRef+"/"+this.urlEncodePath(e)),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.CopyReference.parse(n))})},r.prototype.copyRef=function(t,e){return this.makeCopyReference(t,e)},r.prototype.pullChanges=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=e?e.cursorTag?{cursor:e.cursorTag}:{cursor:e}:{},o=new t.Xhr("POST",this.urls.delta),o.setParams(n).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,n){return r(e,t.PulledChanges.parse(n))})},r.prototype.delta=function(t,e){return this.pullChanges(t,e)},r.prototype.mkdir=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsCreateFolder),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.remove=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsDelete),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.unlink=function(t,e){return this.remove(t,e)},r.prototype["delete"]=function(t,e){return this.remove(t,e)},r.prototype.copy=function(e,r,n){var o,i,s;return n||"function"!=typeof o||(n=o,o=null),i={root:this.fileRoot,to_path:this.normalizePath(r)},e instanceof t.CopyReference?i.from_copy_ref=e.tag:i.from_path=this.normalizePath(e),s=new t.Xhr("POST",this.urls.fileopsCopy),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.move=function(e,r,n){var o,i;return n||"function"!=typeof o||(n=o,o=null),i=new t.Xhr("POST",this.urls.fileopsMove),i.setParams({root:this.fileRoot,from_path:this.normalizePath(e),to_path:this.normalizePath(r)}).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.reset=function(){return this.uid=null,this.oauth.setToken(null,""),this.authState=e.RESET,this.authError=null,this._credentials=null,this},r.prototype.setCredentials=function(t){return this.oauth.reset(t),this.uid=t.uid||null,this.authState=t.authState?t.authState:t.token?e.DONE:e.RESET,this.authError=null,this._credentials=null,this},r.prototype.appHash=function(){return this.oauth.appHash()},r.prototype.setupUrls=function(){return this.fileRoot=this.sandbox?"sandbox":"dropbox",this.urls={requestToken:""+this.apiServer+"/1/oauth/request_token",authorize:""+this.authServer+"/1/oauth/authorize",accessToken:""+this.apiServer+"/1/oauth/access_token",signOut:""+this.apiServer+"/1/unlink_access_token",accountInfo:""+this.apiServer+"/1/account/info",getFile:""+this.fileServer+"/1/files/"+this.fileRoot,postFile:""+this.fileServer+"/1/files/"+this.fileRoot,putFile:""+this.fileServer+"/1/files_put/"+this.fileRoot,metadata:""+this.apiServer+"/1/metadata/"+this.fileRoot,delta:""+this.apiServer+"/1/delta",revisions:""+this.apiServer+"/1/revisions/"+this.fileRoot,restore:""+this.apiServer+"/1/restore/"+this.fileRoot,search:""+this.apiServer+"/1/search/"+this.fileRoot,shares:""+this.apiServer+"/1/shares/"+this.fileRoot,media:""+this.apiServer+"/1/media/"+this.fileRoot,copyRef:""+this.apiServer+"/1/copy_ref/"+this.fileRoot,thumbnails:""+this.fileServer+"/1/thumbnails/"+this.fileRoot,fileopsCopy:""+this.apiServer+"/1/fileops/copy",fileopsCreateFolder:""+this.apiServer+"/1/fileops/create_folder",fileopsDelete:""+this.apiServer+"/1/fileops/delete",fileopsMove:""+this.apiServer+"/1/fileops/move"}},r.ERROR=0,r.RESET=1,r.REQUEST=2,r.AUTHORIZED=3,r.DONE=4,r.SIGNED_OFF=5,r.prototype.urlEncodePath=function(e){return t.Xhr.urlEncodeValue(this.normalizePath(e)).replace(/%2F/gi,"/")},r.prototype.normalizePath=function(t){var e;if("/"===t.substring(0,1)){for(e=1;"/"===t.substring(e,e+1);)e+=1;return t.substring(e)}return t},r.prototype.requestToken=function(e){var r;return r=new t.Xhr("POST",this.urls.requestToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.authorizeUrl=function(e){var r;return r={oauth_token:e,oauth_callback:this.driver.url()},""+this.urls.authorize+"?"+t.Xhr.urlEncode(r)},r.prototype.getAccessToken=function(e){var r;return r=new t.Xhr("POST",this.urls.accessToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.dispatchXhr=function(t,e){var r;return t.setCallback(e),t.prepare(),r=t.xhr,this.filter&&!this.filter(r,t)?r:(t.send(),r)},r.prototype.defaultApiServer=function(){return"https://api.dropbox.com"},r.prototype.defaultAuthServer=function(){return this.apiServer.replace("api.","www.")},r.prototype.defaultFileServer=function(){return this.apiServer.replace("api.","api-content.")},r.prototype.defaultDownloadServer=function(){return this.apiServer.replace("api.","dl.")},r.prototype.computeCredentials=function(){var t;return t={key:this.oauth.key,sandbox:this.sandbox},this.oauth.secret&&(t.secret=this.oauth.secret),this.oauth.token&&(t.token=this.oauth.token,t.tokenSecret=this.oauth.tokenSecret),this.uid&&(t.uid=this.uid),this.authState!==e.ERROR&&this.authState!==e.RESET&&this.authState!==e.DONE&&this.authState!==e.SIGNED_OFF&&(t.authState=this.authState),this.apiServer!==this.defaultApiServer()&&(t.server=this.apiServer),this.authServer!==this.defaultAuthServer()&&(t.authServer=this.authServer),this.fileServer!==this.defaultFileServer()&&(t.fileServer=this.fileServer),this.downloadServer!==this.defaultDownloadServer()&&(t.downloadServer=this.downloadServer),this._credentials=t},r}(),e=t.Client,t.AuthDriver=function(){function t(){}return t.prototype.url=function(){return"https://some.url"},t.prototype.doAuthorize=function(t,e,r,n){return n("oauth-token")},t.prototype.onAuthStateChange=function(t,e){return e()},t}(),t.Drivers={},t.Drivers.BrowserBase=function(){function t(t){this.rememberUser=(null!=t?t.rememberUser:void 0)||!1,this.scope=(null!=t?t.scope:void 0)||"default"}return t.prototype.onAuthStateChange=function(t,r){var n=this;switch(this.setStorageKey(t),t.authState){case e.RESET:return this.loadCredentials(function(e){return e?e.authState?(t.setCredentials(e),r()):n.rememberUser?(t.setCredentials(e),t.getUserInfo(function(e){return e?(t.reset(),n.forgetCredentials(r)):r()})):(n.forgetCredentials(),r()):r()});case e.REQUEST:return this.storeCredentials(t.credentials(),r);case e.DONE:return this.rememberUser?this.storeCredentials(t.credentials(),r):this.forgetCredentials(r);case e.SIGNED_OFF:return this.forgetCredentials(r);case e.ERROR:return this.forgetCredentials(r);default:return r(),this}},t.prototype.setStorageKey=function(t){return this.storageKey="dropbox-auth:"+this.scope+":"+t.appHash(),this},t.prototype.storeCredentials=function(t,e){return localStorage.setItem(this.storageKey,JSON.stringify(t)),e(),this},t.prototype.loadCredentials=function(t){var e;if(e=localStorage.getItem(this.storageKey),!e)return t(null),this;try{t(JSON.parse(e))}catch(r){t(null)}return this},t.prototype.forgetCredentials=function(t){return localStorage.removeItem(this.storageKey),t(),this},t.currentLocation=function(){return window.location.href},t}(),t.Drivers.Redirect=function(r){function n(t){n.__super__.constructor.call(this,t),this.useQuery=(null!=t?t.useQuery:void 0)||!1,this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o,i=this;return o=function(){return function(){return n.__super__.onAuthStateChange.call(i,t,r)}}(),this.setStorageKey(t),t.authState===e.RESET?this.loadCredentials(function(t){return t&&t.authState?t.token===i.locationToken()&&t.authState===e.REQUEST?(t.authState=e.AUTHORIZED,i.storeCredentials(t,o)):i.forgetCredentials(o):o()}):o()},n.prototype.url=function(){return this.receiverUrl},n.prototype.doAuthorize=function(t){return window.location.assign(t)},n.prototype.computeUrl=function(){var e,r,n,o;return o="_dropboxjs_scope="+encodeURIComponent(this.scope),r=t.Drivers.BrowserBase.currentLocation(),-1===r.indexOf("#")?e=null:(n=r.split("#",2),r=n[0],e=n[1]),this.useQuery?r+=-1===r.indexOf("?")?"?"+o:"&"+o:e="?"+o,e?r+"#"+e:r},n.prototype.locationToken=function(){var e,r,n;return e=t.Drivers.BrowserBase.currentLocation(),n="_dropboxjs_scope="+encodeURIComponent(this.scope)+"&",-1===("function"==typeof e.indexOf?e.indexOf(n):void 0)?null:(r=this.tokenRe.exec(e),r?decodeURIComponent(r[2]):null)},n}(t.Drivers.BrowserBase),t.Drivers.Popup=function(r){function n(t){n.__super__.constructor.call(this,t),this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)")}return E(n,r),n.prototype.onAuthStateChange=function(t,r){var o,i=this;return o=function(){return function(){return n.__super__.onAuthStateChange.call(i,t,r)}}(),this.setStorageKey(t),t.authState===e.RESET?this.loadCredentials(function(t){return t&&t.authState?this.forgetCredentials(o):o()}):o()},n.prototype.doAuthorize=function(t,e,r,n){return this.listenForMessage(e,n),this.openWindow(t)},n.prototype.url=function(){return this.receiverUrl},n.prototype.computeUrl=function(e){var r;if(e){if(e.receiverUrl)return e.noFragment||-1!==e.receiverUrl.indexOf("#")?e.receiverUrl:e.receiverUrl+"#";if(e.receiverFile)return r=t.Drivers.BrowserBase.currentLocation().split("/"),r[r.length-1]=e.receiverFile,e.noFragment?r.join("/"):r.join("/")+"#"}return t.Drivers.BrowserBase.currentLocation()},n.prototype.openWindow=function(t){return window.open(t,"_dropboxOauthSigninWindow",this.popupWindowSpec(980,700))},n.prototype.popupWindowSpec=function(t,e){var r,n,o,i,s,a,h,u,l,p;return s=null!=(h=window.screenX)?h:window.screenLeft,a=null!=(u=window.screenY)?u:window.screenTop,i=null!=(l=window.outerWidth)?l:document.documentElement.clientWidth,r=null!=(p=window.outerHeight)?p:document.documentElement.clientHeight,n=Math.round(s+(i-t)/2),o=Math.round(a+(r-e)/2.5),s>n&&(n=s),a>o&&(o=a),"width="+t+",height="+e+","+("left="+n+",top="+o)+"dialog=yes,dependent=yes,scrollbars=yes,location=yes"},n.prototype.listenForMessage=function(t,e){var r,n=this;return r=function(o){var i;return i=n.tokenRe.exec(""+o.data),i&&decodeURIComponent(i[2])===t?(window.removeEventListener("message",r),e()):void 0},window.addEventListener("message",r,!1)},n}(t.Drivers.BrowserBase),t.Drivers.NodeServer=function(){function t(t){this.port=(null!=t?t.port:void 0)||8912,this.faviconFile=(null!=t?t.favicon:void 0)||null,this.fs=require("fs"),this.http=require("http"),this.open=require("open"),this.callbacks={},this.urlRe=RegExp("^/oauth_callback\\?"),this.tokenRe=RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"),this.createApp()}return t.prototype.url=function(){return"http://localhost:"+this.port+"/oauth_callback"},t.prototype.doAuthorize=function(t,e,r,n){return this.callbacks[e]=n,this.openBrowser(t)},t.prototype.openBrowser=function(t){if(!t.match(/^https?:\/\//))throw Error("Not a http/https URL: "+t);return this.open(t)},t.prototype.createApp=function(){var t=this;return this.app=this.http.createServer(function(e,r){return t.doRequest(e,r)}),this.app.listen(this.port)},t.prototype.closeServer=function(){return this.app.close()},t.prototype.doRequest=function(t,e){var r,n,o,i=this;return this.urlRe.exec(t.url)&&(n=this.tokenRe.exec(t.url),n&&(o=decodeURIComponent(n[2]),this.callbacks[o]&&(this.callbacks[o](),delete this.callbacks[o]))),r="",t.on("data",function(t){return r+=t}),t.on("end",function(){return i.faviconFile&&"/favicon.ico"===t.url?i.sendFavicon(e):i.closeBrowser(e)})},t.prototype.closeBrowser=function(t){var e;return e='\n\nPlease close this window.
',t.writeHead(200,{"Content-Length":e.length,"Content-Type":"text/html"}),t.write(e),t.end},t.prototype.sendFavicon=function(t){return this.fs.readFile(this.faviconFile,function(e,r){return t.writeHead(200,{"Content-Length":r.length,"Content-Type":"image/x-icon"}),t.write(r),t.end})},t}(),c=function(t,e){return h(g(b(t),b(e),t.length,e.length))},d=function(t){return h(S(b(t),t.length))},("undefined"==typeof window||null===window)&&(v=require("crypto"),c=function(t,e){var r;return r=v.createHmac("sha1",e),r.update(t),r.digest("base64")},d=function(t){var e;return e=v.createHash("sha1"),e.update(t),e.digest("base64")}),g=function(t,e,r,n){var o,i,s,a;return e.length>16&&(e=S(e,n)),s=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(909522486^e[i]);return r}(),a=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(1549556828^e[i]);return r}(),o=S(s.concat(t),64+r),S(a.concat(o),84)},S=function(t,e){var r,n,o,i,s,h,u,l,p,c,d,f,y,v,m,g,S,b;for(t[e>>2]|=1<<31-((3&e)<<3),t[(e+8>>6<<4)+15]=e<<3,g=Array(80),r=1732584193,o=-271733879,s=-1732584194,u=271733878,p=-1009589776,f=0,m=t.length;m>f;){for(n=r,i=o,h=s,l=u,c=p,y=b=0;80>b;y=++b)g[y]=16>y?t[f+y]:w(g[y-3]^g[y-8]^g[y-14]^g[y-16],1),20>y?(d=o&s|~o&u,v=1518500249):40>y?(d=o^s^u,v=1859775393):60>y?(d=o&s|o&u|s&u,v=-1894007588):(d=o^s^u,v=-899497514),S=a(a(w(r,5),d),a(a(p,g[y]),v)),p=u,u=s,s=w(o,30),o=r,r=S;r=a(r,n),o=a(o,i),s=a(s,h),u=a(u,l),p=a(p,c),f+=16}return[r,o,s,u,p]},w=function(t,e){return t<>>32-e},a=function(t,e){var r,n;return n=(65535&t)+(65535&e),r=(t>>16)+(e>>16)+(n>>16),r<<16|65535&n},h=function(t){var e,r,n,o,i;for(o="",e=0,n=4*t.length;n>e;)r=e,i=(255&t[r>>2]>>(3-(3&r)<<3))<<16,r+=1,i|=(255&t[r>>2]>>(3-(3&r)<<3))<<8,r+=1,i|=255&t[r>>2]>>(3-(3&r)<<3),o+=T[63&i>>18],o+=T[63&i>>12],e+=1,o+=e>=n?"=":T[63&i>>6],e+=1,o+=e>=n?"=":T[63&i],e+=1;return o},T="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",b=function(t){var e,r,n,o,i;for(e=[],n=255,r=o=0,i=t.length;i>=0?i>o:o>i;r=i>=0?++o:--o)e[r>>2]|=(t.charCodeAt(r)&n)<<(3-(3&r)<<3);return e},t.Oauth=function(){function e(t){this.key=this.k=null,this.secret=this.s=null,this.token=null,this.tokenSecret=null,this._appHash=null,this.reset(t)}return e.prototype.reset=function(t){var e,r,n,o;if(t.secret)this.k=this.key=t.key,this.s=this.secret=t.secret,this._appHash=null;else if(t.key)this.key=t.key,this.secret=null,n=u(m(this.key).split("|",2)[1]),o=n.split("?",2),e=o[0],r=o[1],this.k=decodeURIComponent(e),this.s=decodeURIComponent(r),this._appHash=null;else if(!this.k)throw Error("No API key supplied");return t.token?this.setToken(t.token,t.tokenSecret):this.setToken(null,"")},e.prototype.setToken=function(e,r){if(e&&!r)throw Error("No secret supplied with the user token");return this.token=e,this.tokenSecret=r||"",this.hmacKey=t.Xhr.urlEncodeValue(this.s)+"&"+t.Xhr.urlEncodeValue(r),null},e.prototype.authHeader=function(e,r,n){var o,i,s,a,h,u;this.addAuthParams(e,r,n),i=[];for(s in n)a=n[s],"oauth_"===s.substring(0,6)&&i.push(s);for(i.sort(),o=[],h=0,u=i.length;u>h;h++)s=i[h],o.push(t.Xhr.urlEncodeValue(s)+'="'+t.Xhr.urlEncodeValue(n[s])+'"'),delete n[s];return"OAuth "+o.join(",")},e.prototype.addAuthParams=function(t,e,r){return this.boilerplateParams(r),r.oauth_signature=this.signature(t,e,r),r},e.prototype.boilerplateParams=function(t){return t.oauth_consumer_key=this.k,t.oauth_nonce=this.nonce(),t.oauth_signature_method="HMAC-SHA1",this.token&&(t.oauth_token=this.token),t.oauth_timestamp=Math.floor(Date.now()/1e3),t.oauth_version="1.0",t},e.prototype.nonce=function(){return Date.now().toString(36)+Math.random().toString(36)},e.prototype.signature=function(e,r,n){var o;return o=e.toUpperCase()+"&"+t.Xhr.urlEncodeValue(r)+"&"+t.Xhr.urlEncodeValue(t.Xhr.urlEncode(n)),c(o,this.hmacKey)},e.prototype.appHash=function(){return this._appHash?this._appHash:this._appHash=d(this.k).replace(/\=/g,"")},e}(),null==Date.now&&(Date.now=function(){return(new Date).getTime()}),m=function(t,e){var r,n,o,i,s,a,h,l,p,c,d,y;for(e?(e=[encodeURIComponent(t),encodeURIComponent(e)].join("?"),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length/2;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(16*(15&t.charCodeAt(2*r))+(15&t.charCodeAt(2*r+1)));return o}()):(c=t.split("|",2),t=c[0],e=c[1],t=u(t),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}(),e=u(e)),i=function(){for(y=[],l=0;256>l;l++)y.push(l);return y}.apply(this),a=0,s=p=0;256>p;s=++p)a=(a+i[r]+t[s%t.length])%256,d=[i[a],i[s]],i[s]=d[0],i[a]=d[1];return s=a=0,o=function(){var t,r,o,u;for(u=[],h=t=0,r=e.length;r>=0?r>t:t>r;h=r>=0?++t:--t)s=(s+1)%256,a=(a+i[s])%256,o=[i[a],i[s]],i[s]=o[0],i[a]=o[1],n=i[(i[s]+i[a])%256],u.push(String.fromCharCode((n^e.charCodeAt(h))%256));return u}(),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(String.fromCharCode(t[r]));return o}(),[f(t.join("")),f(o.join(""))].join("|")},t.PulledChanges=function(){function e(e){var r;this.blankSlate=e.reset||!1,this.cursorTag=e.cursor,this.shouldPullAgain=e.has_more,this.shouldBackOff=!this.shouldPullAgain,this.changes=e.cursor&&e.cursor.length?function(){var n,o,i,s;for(i=e.entries,s=[],n=0,o=i.length;o>n;n++)r=i[n],s.push(t.PullChange.parse(r));return s}():[]}return e.parse=function(e){return e&&"object"==typeof e?new t.PulledChanges(e):e},e.prototype.blankSlate=void 0,e.prototype.cursorTag=void 0,e.prototype.changes=void 0,e.prototype.shouldPullAgain=void 0,e.prototype.shouldBackOff=void 0,e}(),t.PullChange=function(){function e(e){this.path=e[0],this.stat=t.Stat.parse(e[1]),this.stat?this.wasRemoved=!1:(this.stat=null,this.wasRemoved=!0)}return e.parse=function(e){return e&&"object"==typeof e?new t.PullChange(e):e},e.prototype.path=void 0,e.prototype.wasRemoved=void 0,e.prototype.stat=void 0,e}(),t.PublicUrl=function(){function e(t,e){this.url=t.url,this.expiresAt=new Date(Date.parse(t.expires)),this.isDirect=e===!0?!0:e===!1?!1:864e5>=Date.now()-this.expiresAt,this.isPreview=!this.isDirect}return e.parse=function(e,r){return e&&"object"==typeof e?new t.PublicUrl(e,r):e},e.prototype.url=void 0,e.prototype.expiresAt=void 0,e.prototype.isDirect=void 0,e.prototype.isPreview=void 0,e}(),t.CopyReference=function(){function e(t){"object"==typeof t?(this.tag=t.copy_ref,this.expiresAt=new Date(Date.parse(t.expires))):(this.tag=t,this.expiresAt=new Date)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.CopyReference(e)},e.prototype.tag=void 0,e.prototype.expiresAt=void 0,e}(),t.Stat=function(){function e(t){var e,r,n,o;switch(this.path=t.path,"/"!==this.path.substring(0,1)&&(this.path="/"+this.path),e=this.path.length-1,e>=0&&"/"===this.path.substring(e)&&(this.path=this.path.substring(0,e)),r=this.path.lastIndexOf("/"),this.name=this.path.substring(r+1),this.isFolder=t.is_dir||!1,this.isFile=!this.isFolder,this.isRemoved=t.is_deleted||!1,this.typeIcon=t.icon,this.modifiedAt=(null!=(n=t.modified)?n.length:void 0)?new Date(Date.parse(t.modified)):null,this.clientModifiedAt=(null!=(o=t.client_mtime)?o.length:void 0)?new Date(Date.parse(t.client_mtime)):null,t.root){case"dropbox":this.inAppFolder=!1;break;case"app_folder":this.inAppFolder=!0;break;default:this.inAppFolder=null}this.size=t.bytes||0,this.humanSize=t.size||"",this.hasThumbnail=t.thumb_exists||!1,this.isFolder?(this.versionTag=t.hash,this.mimeType=t.mime_type||"inode/directory"):(this.versionTag=t.rev,this.mimeType=t.mime_type||"application/octet-stream")}return e.parse=function(e){return e&&"object"==typeof e?new t.Stat(e):e},e.prototype.path=null,e.prototype.name=null,e.prototype.inAppFolder=null,e.prototype.isFolder=null,e.prototype.isFile=null,e.prototype.isRemoved=null,e.prototype.typeIcon=null,e.prototype.versionTag=null,e.prototype.mimeType=null,e.prototype.size=null,e.prototype.humanSize=null,e.prototype.hasThumbnail=null,e.prototype.modifiedAt=null,e.prototype.clientModifiedAt=null,e}(),t.UserInfo=function(){function e(t){var e;this.name=t.display_name,this.email=t.email,this.countryCode=t.country||null,this.uid=""+t.uid,t.public_app_url?(this.publicAppUrl=t.public_app_url,e=this.publicAppUrl.length-1,e>=0&&"/"===this.publicAppUrl.substring(e)&&(this.publicAppUrl=this.publicAppUrl.substring(0,e))):this.publicAppUrl=null,this.referralUrl=t.referral_link,this.quota=t.quota_info.quota,this.privateBytes=t.quota_info.normal||0,this.sharedBytes=t.quota_info.shared||0,this.usedQuota=this.privateBytes+this.sharedBytes}return e.parse=function(e){return e&&"object"==typeof e?new t.UserInfo(e):e},e.prototype.name=null,e.prototype.email=null,e.prototype.countryCode=null,e.prototype.uid=null,e.prototype.referralUrl=null,e.prototype.publicAppUrl=null,e.prototype.quota=null,e.prototype.usedQuota=null,e.prototype.privateBytes=null,e.prototype.sharedBytes=null,e}(),"undefined"!=typeof window&&null!==window?(!window.XDomainRequest||"withCredentials"in new XMLHttpRequest?(s=window.XMLHttpRequest,i=!1,n=-1===window.navigator.userAgent.indexOf("Firefox")):(s=window.XDomainRequest,i=!0,n=!1),o=!0):(s=require("xmlhttprequest").XMLHttpRequest,i=!1,n=!1,o=!1),r="undefined"==typeof Uint8Array?null:new Uint8Array(0).__proto__.__proto__.constructor,t.Xhr=function(){function e(t,e){this.method=t,this.isGet="GET"===this.method,this.url=e,this.headers={},this.params=null,this.body=null,this.preflight=!(this.isGet||"POST"===this.method),this.signed=!1,this.responseType=null,this.callback=null,this.xhr=null}return e.Request=s,e.ieMode=i,e.canSendForms=n,e.doesPreflight=o,e.ArrayBufferView=r,e.prototype.setParams=function(t){if(this.signed)throw Error("setParams called after addOauthParams or addOauthHeader");if(this.params)throw Error("setParams cannot be called twice");return this.params=t,this},e.prototype.setCallback=function(t){return this.callback=t,this},e.prototype.signWithOauth=function(e){return t.Xhr.ieMode||t.Xhr.doesPreflight&&!this.preflight?this.addOauthParams(e):this.addOauthHeader(e)},e.prototype.addOauthParams=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),t.addAuthParams(this.method,this.url,this.params),this.signed=!0,this},e.prototype.addOauthHeader=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),this.signed=!0,this.setHeader("Authorization",t.authHeader(this.method,this.url,this.params))},e.prototype.setBody=function(t){if(this.isGet)throw Error("setBody cannot be called on GET requests");if(null!==this.body)throw Error("Request already has a body");return this.preflight||"undefined"!=typeof FormData&&t instanceof FormData||(this.preflight=!0),this.body=t,this},e.prototype.setResponseType=function(t){return this.responseType=t,this
-},e.prototype.setHeader=function(t,e){var r;if(this.headers[t])throw r=this.headers[t],Error("HTTP header "+t+" already set to "+r);if("Content-Type"===t)throw Error("Content-Type is automatically computed based on setBody");return this.preflight=!0,this.headers[t]=e,this},e.prototype.setFileField=function(e,r,n,o){var i,s;if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");return"object"==typeof n&&"undefined"!=typeof Blob?("undefined"!=typeof ArrayBuffer&&null!==ArrayBuffer&&n instanceof ArrayBuffer&&(n=new Uint8Array(n)),t.Xhr.ArrayBufferView&&n instanceof t.Xhr.ArrayBufferView&&(o||(o="application/octet-stream"),n=new Blob([n],{type:o})),"undefined"!=typeof File&&n instanceof File&&(n=new Blob([n],{type:n.type})),s=n instanceof Blob):s=!1,s?(this.body=new FormData,this.body.append(e,n,r)):(o||(o="application/octet-stream"),i=this.multipartBoundary(),this.headers["Content-Type"]="multipart/form-data; boundary="+i,this.body=["--",i,"\r\n",'Content-Disposition: form-data; name="',e,'"; filename="',r,'"\r\n',"Content-Type: ",o,"\r\n","Content-Transfer-Encoding: binary\r\n\r\n",n,"\r\n","--",i,"--","\r\n"].join(""))},e.prototype.multipartBoundary=function(){return[Date.now().toString(36),Math.random().toString(36)].join("----")},e.prototype.paramsToUrl=function(){var e;return this.params&&(e=t.Xhr.urlEncode(this.params),0!==e.length&&(this.url=[this.url,"?",e].join("")),this.params=null),this},e.prototype.paramsToBody=function(){if(this.params){if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");this.headers["Content-Type"]="application/x-www-form-urlencoded",this.body=t.Xhr.urlEncode(this.params),this.params=null}return this},e.prototype.prepare=function(){var e,r,n,o,i=this;if(r=t.Xhr.ieMode,this.isGet||null!==this.body||r?(this.paramsToUrl(),null!==this.body&&"string"==typeof this.body&&(this.headers["Content-Type"]="text/plain; charset=utf8")):this.paramsToBody(),this.xhr=new t.Xhr.Request,r?(this.xhr.onload=function(){return i.onLoad()},this.xhr.onerror=function(){return i.onError()}):this.xhr.onreadystatechange=function(){return i.onReadyStateChange()},this.xhr.open(this.method,this.url,!0),!r){o=this.headers;for(e in o)_.call(o,e)&&(n=o[e],this.xhr.setRequestHeader(e,n))}return this.responseType&&("b"===this.responseType?this.xhr.overrideMimeType&&this.xhr.overrideMimeType("text/plain; charset=x-user-defined"):this.xhr.responseType=this.responseType),this},e.prototype.send=function(e){var r;if(this.callback=e||this.callback,null!==this.body){r=this.body,t.Xhr.ArrayBufferView&&r instanceof ArrayBuffer&&(r=new Uint8Array(r));try{this.xhr.send(r)}catch(n){if(!("undefined"!=typeof Blob&&t.Xhr.ArrayBufferView&&r instanceof t.Xhr.ArrayBufferView))throw n;r=new Blob([r],{type:"application/octet-stream"}),this.xhr.send(r)}}else this.xhr.send();return this},e.urlEncode=function(t){var e,r,n;e=[];for(r in t)n=t[r],e.push(this.urlEncodeValue(r)+"="+this.urlEncodeValue(n));return e.sort().join("&")},e.urlEncodeValue=function(t){return encodeURIComponent(""+t).replace(/\!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")},e.urlDecode=function(t){var e,r,n,o,i,s;for(r={},s=t.split("&"),o=0,i=s.length;i>o;o++)n=s[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r},e.prototype.onReadyStateChange=function(){var e,r,n,o,i,s,a,h,u;if(4!==this.xhr.readyState)return!0;if(200>this.xhr.status||this.xhr.status>=300)return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0;if(s=this.xhr.getResponseHeader("x-dropbox-metadata"),null!=s?s.length:void 0)try{i=JSON.parse(s)}catch(l){i=void 0}else i=void 0;if(this.responseType){if("b"===this.responseType){for(n=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,r=[],o=h=0,u=n.length;u>=0?u>h:h>u;o=u>=0?++h:--h)r.push(String.fromCharCode(255&n.charCodeAt(o)));a=r.join(""),this.callback(null,a,i)}else this.callback(null,this.xhr.response,i);return!0}switch(a=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,this.xhr.getResponseHeader("Content-Type")){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(a),i);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(a),i);break;default:this.callback(null,a,i)}return!0},e.prototype.onLoad=function(){var e;switch(e=this.xhr.responseText,this.xhr.contentType){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(e),void 0);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(e),void 0);break;default:this.callback(null,e,void 0)}return!0},e.prototype.onError=function(){var e;return e=new t.ApiError(this.xhr,this.method,this.url),this.callback(e),!0},e}(),null!=("undefined"!=typeof module&&null!==module?module.exports:void 0))module.exports=t;else{if("undefined"==typeof window||null===window)throw Error("This library only supports node.js and modern browsers.");window.Dropbox=t}t.atob=u,t.btoa=f,t.hmac=c,t.sha1=d,t.encodeKey=m}).call(this);
\ No newline at end of file
+(function(){var t,e,r,n,o,i,s,a,h,u,l,p,c,d,f,y,v,m,g,w,S,b,_,E,C,x,R={}.hasOwnProperty,T=function(t,e){function r(){this.constructor=t}for(var n in e)R.call(e,n)&&(t[n]=e[n]);return r.prototype=e.prototype,t.prototype=new r,t.__super__=e.prototype,t},k=[].indexOf||function(t){for(var e=0,r=this.length;r>e;e++)if(e in this&&this[e]===t)return e;return-1};if(t=function(){function t(t){this.client=new n(t)}return t}(),t.ApiError=function(){function t(t,e,r){var n;if(this.method=e,this.url=r,this.status=t.status,t.responseType)try{n=t.response||t.responseText}catch(o){try{n=t.responseText}catch(o){n=null}}else try{n=t.responseText}catch(o){n=null}if(n)try{this.responseText=""+n,this.response=JSON.parse(n)}catch(o){this.response=null}else this.responseText="(no response)",this.response=null}return t.prototype.status=void 0,t.prototype.method=void 0,t.prototype.url=void 0,t.prototype.responseText=void 0,t.prototype.response=void 0,t.prototype.toString=function(){return"Dropbox API error "+this.status+" from "+this.method+" "+this.url+" :: "+this.responseText},t.prototype.inspect=function(){return""+this},t}(),"undefined"!=typeof window&&null!==window?window.atob&&window.btoa?(c=function(t){return window.atob(t)},m=function(t){return window.btoa(t)}):(f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",g=function(t,e,r){var n,o;for(o=3-e,t<<=8*o,n=3;n>=o;)r.push(f.charAt(63&t>>6*n)),n-=1;for(n=e;3>n;)r.push("="),n+=1;return null},d=function(t,e,r){var n,o;for(o=4-e,t<<=6*o,n=2;n>=o;)r.push(String.fromCharCode(255&t>>8*n)),n-=1;return null},m=function(t){var e,r,n,o,i,s;for(o=[],e=0,r=0,n=i=0,s=t.length;s>=0?s>i:i>s;n=s>=0?++i:--i)e=e<<8|t.charCodeAt(n),r+=1,3===r&&(g(e,r,o),e=r=0);return r>0&&g(e,r,o),o.join("")},c=function(t){var e,r,n,o,i,s,a;for(i=[],e=0,n=0,o=s=0,a=t.length;(a>=0?a>s:s>a)&&(r=t.charAt(o),"="!==r);o=a>=0?++s:--s)e=e<<6|f.indexOf(r),n+=1,4===n&&(d(e,n,i),e=n=0);return n>0&&d(e,n,i),i.join("")}):(c=function(t){var e,r;return e=new Buffer(t,"base64"),function(){var t,n,o;for(o=[],r=t=0,n=e.length;n>=0?n>t:t>n;r=n>=0?++t:--t)o.push(String.fromCharCode(e[r]));return o}().join("")},m=function(t){var e,r;return e=new Buffer(function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}()),e.toString("base64")}),t.Client=function(){function e(e){this.sandbox=e.sandbox||!1,this.apiServer=e.server||this.defaultApiServer(),this.authServer=e.authServer||this.defaultAuthServer(),this.fileServer=e.fileServer||this.defaultFileServer(),this.downloadServer=e.downloadServer||this.defaultDownloadServer(),this.onXhr=new t.EventSource({cancelable:!0}),this.onError=new t.EventSource,this.onAuthStateChange=new t.EventSource,this.oauth=new t.Oauth(e),this.driver=null,this.filter=null,this.uid=null,this.authState=null,this.authError=null,this._credentials=null,this.setCredentials(e),this.setupUrls()}return e.prototype.authDriver=function(t){return this.driver=t,this},e.prototype.onXhr=null,e.prototype.onError=null,e.prototype.onAuthStateChange=null,e.prototype.dropboxUid=function(){return this.uid},e.prototype.credentials=function(){return this._credentials||this.computeCredentials(),this._credentials},e.prototype.authenticate=function(t){var e,r,o=this;if(e=null,!this.driver&&this.authState!==n.DONE)throw Error("Call authDriver to set an authentication driver");return r=function(){var i;if(e!==o.authState&&(null!==e&&o.onAuthStateChange.dispatch(o),e=o.authState,o.driver&&o.driver.onAuthStateChange))return o.driver.onAuthStateChange(o,r);switch(o.authState){case n.RESET:return o.requestToken(function(t,e){var i,s;return t?(o.authError=t,o.authState=n.ERROR):(i=e.oauth_token,s=e.oauth_token_secret,o.oauth.setToken(i,s),o.authState=n.REQUEST),o._credentials=null,r()});case n.REQUEST:return i=o.authorizeUrl(o.oauth.token),o.driver.doAuthorize(i,o.oauth.token,o.oauth.tokenSecret,function(){return o.authState=n.AUTHORIZED,o._credentials=null,r()});case n.AUTHORIZED:return o.getAccessToken(function(t,e){return t?(o.authError=t,o.authState=n.ERROR):(o.oauth.setToken(e.oauth_token,e.oauth_token_secret),o.uid=e.uid,o.authState=n.DONE),o._credentials=null,r()});case n.DONE:return t(null,o);case n.SIGNED_OFF:return o.authState=n.RESET,o.reset(),r();case n.ERROR:return t(o.authError)}},r(),this},e.prototype.isAuthenticated=function(){return this.authState===n.DONE},e.prototype.signOut=function(e){var r,o=this;return r=new t.Xhr("POST",this.urls.signOut),r.signWithOauth(this.oauth),this.dispatchXhr(r,function(t){return t?e(t):(o.authState=n.RESET,o.reset(),o.authState=n.SIGNED_OFF,o.onAuthStateChange.dispatch(o),o.driver.onAuthStateChange?o.driver.onAuthStateChange(o,function(){return e(t)}):e(t))})},e.prototype.signOff=function(t){return this.signOut(t)},e.prototype.getUserInfo=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=!1,e&&e.httpCache&&(n=!0),o=new t.Xhr("GET",this.urls.accountInfo),o.signWithOauth(this.oauth,n),this.dispatchXhr(o,function(e,n){return r(e,t.UserInfo.parse(n),n)})},e.prototype.readFile=function(e,r,n){var o,i,s,a,h,u,l;return n||"function"!=typeof r||(n=r,r=null),i={},u=null,a=null,o=!1,r&&(r.versionTag?i.rev=r.versionTag:r.rev&&(i.rev=r.rev),r.arrayBuffer?u="arraybuffer":r.blob?u="blob":r.binary&&(u="b"),r.length?(null!=r.start?(h=r.start,s=r.start+r.length-1):(h="",s=r.length),a="bytes="+h+"-"+s):null!=r.start&&(a="bytes="+r.start+"-"),r.httpCache&&(o=!0)),l=new t.Xhr("GET",""+this.urls.getFile+"/"+this.urlEncodePath(e)),l.setParams(i).signWithOauth(this.oauth,o),l.setResponseType(u),a&&l.setHeader("Range",a),this.dispatchXhr(l,function(e,r,o){return n(e,r,t.Stat.parse(o))})},e.prototype.writeFile=function(e,r,n,o){var i;return o||"function"!=typeof n||(o=n,n=null),i=t.Xhr.canSendForms&&"object"==typeof r,i?this.writeFileUsingForm(e,r,n,o):this.writeFileUsingPut(e,r,n,o)},e.prototype.writeFileUsingForm=function(e,r,n,o){var i,s,a,h;return a=e.lastIndexOf("/"),-1===a?(i=e,e=""):(i=e.substring(a),e=e.substring(0,a)),s={file:i},n&&(n.noOverwrite&&(s.overwrite="false"),n.lastVersionTag?s.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(s.parent_rev=n.parentRev||n.parent_rev)),h=new t.Xhr("POST",""+this.urls.postFile+"/"+this.urlEncodePath(e)),h.setParams(s).signWithOauth(this.oauth).setFileField("file",i,r,"application/octet-stream"),delete s.file,this.dispatchXhr(h,function(e,r){return o(e,t.Stat.parse(r))})},e.prototype.writeFileUsingPut=function(e,r,n,o){var i,s;return i={},n&&(n.noOverwrite&&(i.overwrite="false"),n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev)),s=new t.Xhr("POST",""+this.urls.putFile+"/"+this.urlEncodePath(e)),s.setBody(r).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},e.prototype.resumableUploadStep=function(e,r,n){var o,i;return r?(o={offset:r.offset},r.tag&&(o.upload_id=r.tag)):o={offset:0},i=new t.Xhr("POST",this.urls.chunkedUpload),i.setBody(e).setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return e&&400===e.status&&e.response.upload_id&&e.response.offset?n(null,t.UploadCursor.parse(e.response)):n(e,t.UploadCursor.parse(r))})},e.prototype.resumableUploadFinish=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={upload_id:r.tag},n&&(n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev),n.noOverwrite&&(i.autorename=!0)),s=new t.Xhr("POST",""+this.urls.commitChunkedUpload+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},e.prototype.stat=function(e,r,n){var o,i,s;return n||"function"!=typeof r||(n=r,r=null),i={},o=!1,r&&(null!=r.version&&(i.rev=r.version),(r.removed||r.deleted)&&(i.include_deleted="true"),r.readDir&&(i.list="true",r.readDir!==!0&&(i.file_limit=""+r.readDir)),r.cacheHash&&(i.hash=r.cacheHash),r.httpCache&&(o=!0)),i.include_deleted||(i.include_deleted="false"),i.list||(i.list="false"),s=new t.Xhr("GET",""+this.urls.metadata+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth,o),this.dispatchXhr(s,function(e,r){var o,i,s;return s=t.Stat.parse(r),o=(null!=r?r.contents:void 0)?function(){var e,n,o,s;for(o=r.contents,s=[],e=0,n=o.length;n>e;e++)i=o[e],s.push(t.Stat.parse(i));return s}():void 0,n(e,s,o)})},e.prototype.readdir=function(t,e,r){var n;return r||"function"!=typeof e||(r=e,e=null),n={readDir:!0},e&&(null!=e.limit&&(n.readDir=e.limit),e.versionTag&&(n.versionTag=e.versionTag),(e.removed||e.deleted)&&(n.removed=e.removed||e.deleted),e.httpCache&&(n.httpCache=e.httpCache)),this.stat(t,n,function(t,e,n){var o,i;return o=n?function(){var t,e,r;for(r=[],t=0,e=n.length;e>t;t++)i=n[t],r.push(i.name);return r}():null,r(t,o,e,n)})},e.prototype.metadata=function(t,e,r){return this.stat(t,e,r)},e.prototype.makeUrl=function(e,r,n){var o,i,s,a,h;return n||"function"!=typeof r||(n=r,r=null),i=r&&(r["long"]||r.longUrl||r.downloadHack)?{short_url:"false"}:{},e=this.urlEncodePath(e),s=""+this.urls.shares+"/"+e,o=!1,a=!1,r&&(r.downloadHack?(o=!0,a=!0):r.download&&(o=!0,s=""+this.urls.media+"/"+e)),h=new t.Xhr("POST",s).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(h,function(e,r){return a&&r&&r.url&&(r.url=r.url.replace(this.authServer,this.downloadServer)),n(e,t.PublicUrl.parse(r,o))})},e.prototype.history=function(e,r,n){var o,i,s;return n||"function"!=typeof r||(n=r,r=null),i={},o=!1,r&&(null!=r.limit&&(i.rev_limit=r.limit),r.httpCache&&(o=!0)),s=new t.Xhr("GET",""+this.urls.revisions+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth,o),this.dispatchXhr(s,function(e,r){var o,i;return i=r?function(){var e,n,i;for(i=[],e=0,n=r.length;n>e;e++)o=r[e],i.push(t.Stat.parse(o));return i}():void 0,n(e,i)})},e.prototype.revisions=function(t,e,r){return this.history(t,e,r)},e.prototype.thumbnailUrl=function(t,e){var r;return r=this.thumbnailXhr(t,e),r.paramsToUrl().url},e.prototype.readThumbnail=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o="b",r&&r.blob&&(o="blob"),i=this.thumbnailXhr(e,r),i.setResponseType(o),this.dispatchXhr(i,function(e,r,o){return n(e,r,t.Stat.parse(o))})},e.prototype.thumbnailXhr=function(e,r){var n,o;return n={},r&&(r.format?n.format=r.format:r.png&&(n.format="png"),r.size&&(n.size=r.size)),o=new t.Xhr("GET",""+this.urls.thumbnails+"/"+this.urlEncodePath(e)),o.setParams(n).signWithOauth(this.oauth)},e.prototype.revertFile=function(e,r,n){var o;return o=new t.Xhr("POST",""+this.urls.restore+"/"+this.urlEncodePath(e)),o.setParams({rev:r}).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,r){return n(e,t.Stat.parse(r))})},e.prototype.restore=function(t,e,r){return this.revertFile(t,e,r)},e.prototype.findByName=function(e,r,n,o){var i,s,a;return o||"function"!=typeof n||(o=n,n=null),s={query:r},i=!1,n&&(null!=n.limit&&(s.file_limit=n.limit),(n.removed||n.deleted)&&(s.include_deleted=!0),n.httpCache&&(i=!0)),a=new t.Xhr("GET",""+this.urls.search+"/"+this.urlEncodePath(e)),a.setParams(s).signWithOauth(this.oauth,i),this.dispatchXhr(a,function(e,r){var n,i;return i=r?function(){var e,o,i;for(i=[],e=0,o=r.length;o>e;e++)n=r[e],i.push(t.Stat.parse(n));return i}():void 0,o(e,i)})},e.prototype.search=function(t,e,r,n){return this.findByName(t,e,r,n)},e.prototype.makeCopyReference=function(e,r){var n;return n=new t.Xhr("GET",""+this.urls.copyRef+"/"+this.urlEncodePath(e)),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.CopyReference.parse(n))})},e.prototype.copyRef=function(t,e){return this.makeCopyReference(t,e)},e.prototype.pullChanges=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=e?e.cursorTag?{cursor:e.cursorTag}:{cursor:e}:{},o=new t.Xhr("POST",this.urls.delta),o.setParams(n).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,n){return r(e,t.PulledChanges.parse(n))})},e.prototype.delta=function(t,e){return this.pullChanges(t,e)},e.prototype.mkdir=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsCreateFolder),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},e.prototype.remove=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsDelete),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},e.prototype.unlink=function(t,e){return this.remove(t,e)},e.prototype["delete"]=function(t,e){return this.remove(t,e)},e.prototype.copy=function(e,r,n){var o,i,s;return n||"function"!=typeof o||(n=o,o=null),i={root:this.fileRoot,to_path:this.normalizePath(r)},e instanceof t.CopyReference?i.from_copy_ref=e.tag:i.from_path=this.normalizePath(e),s=new t.Xhr("POST",this.urls.fileopsCopy),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return n(e,t.Stat.parse(r))})},e.prototype.move=function(e,r,n){var o,i;return n||"function"!=typeof o||(n=o,o=null),i=new t.Xhr("POST",this.urls.fileopsMove),i.setParams({root:this.fileRoot,from_path:this.normalizePath(e),to_path:this.normalizePath(r)}).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return n(e,t.Stat.parse(r))})},e.prototype.reset=function(){var t;return this.uid=null,this.oauth.setToken(null,""),t=this.authState,this.authState=n.RESET,t!==this.authState&&this.onAuthStateChange.dispatch(this),this.authError=null,this._credentials=null,this},e.prototype.setCredentials=function(t){var e;return e=this.authState,this.oauth.reset(t),this.uid=t.uid||null,this.authState=t.authState?t.authState:t.token?n.DONE:n.RESET,this.authError=null,this._credentials=null,e!==this.authState&&this.onAuthStateChange.dispatch(this),this},e.prototype.appHash=function(){return this.oauth.appHash()},e.prototype.setupUrls=function(){return this.fileRoot=this.sandbox?"sandbox":"dropbox",this.urls={requestToken:""+this.apiServer+"/1/oauth/request_token",authorize:""+this.authServer+"/1/oauth/authorize",accessToken:""+this.apiServer+"/1/oauth/access_token",signOut:""+this.apiServer+"/1/unlink_access_token",accountInfo:""+this.apiServer+"/1/account/info",getFile:""+this.fileServer+"/1/files/"+this.fileRoot,postFile:""+this.fileServer+"/1/files/"+this.fileRoot,putFile:""+this.fileServer+"/1/files_put/"+this.fileRoot,metadata:""+this.apiServer+"/1/metadata/"+this.fileRoot,delta:""+this.apiServer+"/1/delta",revisions:""+this.apiServer+"/1/revisions/"+this.fileRoot,restore:""+this.apiServer+"/1/restore/"+this.fileRoot,search:""+this.apiServer+"/1/search/"+this.fileRoot,shares:""+this.apiServer+"/1/shares/"+this.fileRoot,media:""+this.apiServer+"/1/media/"+this.fileRoot,copyRef:""+this.apiServer+"/1/copy_ref/"+this.fileRoot,thumbnails:""+this.fileServer+"/1/thumbnails/"+this.fileRoot,chunkedUpload:""+this.fileServer+"/1/chunked_upload",commitChunkedUpload:""+this.fileServer+"/1/commit_chunked_upload/"+this.fileRoot,fileopsCopy:""+this.apiServer+"/1/fileops/copy",fileopsCreateFolder:""+this.apiServer+"/1/fileops/create_folder",fileopsDelete:""+this.apiServer+"/1/fileops/delete",fileopsMove:""+this.apiServer+"/1/fileops/move"}},e.prototype.authState=null,e.ERROR=0,e.RESET=1,e.REQUEST=2,e.AUTHORIZED=3,e.DONE=4,e.SIGNED_OFF=5,e.prototype.urlEncodePath=function(e){return t.Xhr.urlEncodeValue(this.normalizePath(e)).replace(/%2F/gi,"/")},e.prototype.normalizePath=function(t){var e;if("/"===t.substring(0,1)){for(e=1;"/"===t.substring(e,e+1);)e+=1;return t.substring(e)}return t},e.prototype.requestToken=function(e){var r;return r=new t.Xhr("POST",this.urls.requestToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},e.prototype.authorizeUrl=function(e){var r;return r={oauth_token:e,oauth_callback:this.driver.url()},""+this.urls.authorize+"?"+t.Xhr.urlEncode(r)},e.prototype.getAccessToken=function(e){var r;return r=new t.Xhr("POST",this.urls.accessToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},e.prototype.dispatchXhr=function(t,e){var r;return t.setCallback(e),t.onError=this.onError,t.prepare(),r=t.xhr,this.onXhr.dispatch(t)&&t.send(),r},e.prototype.defaultApiServer=function(){return"https://api.dropbox.com"},e.prototype.defaultAuthServer=function(){return this.apiServer.replace("api.","www.")},e.prototype.defaultFileServer=function(){return this.apiServer.replace("api.","api-content.")},e.prototype.defaultDownloadServer=function(){return this.apiServer.replace("api.","dl.")},e.prototype.computeCredentials=function(){var t;return t={key:this.oauth.key,sandbox:this.sandbox},this.oauth.secret&&(t.secret=this.oauth.secret),this.oauth.token&&(t.token=this.oauth.token,t.tokenSecret=this.oauth.tokenSecret),this.uid&&(t.uid=this.uid),this.authState!==n.ERROR&&this.authState!==n.RESET&&this.authState!==n.DONE&&this.authState!==n.SIGNED_OFF&&(t.authState=this.authState),this.apiServer!==this.defaultApiServer()&&(t.server=this.apiServer),this.authServer!==this.defaultAuthServer()&&(t.authServer=this.authServer),this.fileServer!==this.defaultFileServer()&&(t.fileServer=this.fileServer),this.downloadServer!==this.defaultDownloadServer()&&(t.downloadServer=this.downloadServer),this._credentials=t},e}(),n=t.Client,t.AuthDriver=function(){function t(){}return t.prototype.url=function(){return"https://some.url"},t.prototype.doAuthorize=function(t,e,r,n){return n("oauth-token")},t.prototype.onAuthStateChange=function(t,e){return e()},t}(),t.Drivers={},t.Drivers.BrowserBase=function(){function t(t){this.rememberUser=(null!=t?t.rememberUser:void 0)||!1,this.scope=(null!=t?t.scope:void 0)||"default",this.storageKey=null}return t.prototype.onAuthStateChange=function(t,e){var r=this;switch(this.setStorageKey(t),t.authState){case n.RESET:return this.loadCredentials(function(n){return n?n.authState?(t.setCredentials(n),e()):r.rememberUser?(t.setCredentials(n),t.getUserInfo(function(n){return n?(t.reset(),r.forgetCredentials(e)):e()})):(r.forgetCredentials(),e()):e()});case n.REQUEST:return this.storeCredentials(t.credentials(),e);case n.DONE:return this.rememberUser?this.storeCredentials(t.credentials(),e):this.forgetCredentials(e);case n.SIGNED_OFF:return this.forgetCredentials(e);case n.ERROR:return this.forgetCredentials(e);default:return e(),this}},t.prototype.setStorageKey=function(t){return this.storageKey="dropbox-auth:"+this.scope+":"+t.appHash(),this},t.prototype.storeCredentials=function(t,e){return localStorage.setItem(this.storageKey,JSON.stringify(t)),e(),this},t.prototype.loadCredentials=function(t){var e;if(e=localStorage.getItem(this.storageKey),!e)return t(null),this;try{t(JSON.parse(e))}catch(r){t(null)}return this},t.prototype.forgetCredentials=function(t){return localStorage.removeItem(this.storageKey),t(),this},t.currentLocation=function(){return window.location.href},t}(),t.Drivers.Redirect=function(e){function r(t){r.__super__.constructor.call(this,t),this.useQuery=(null!=t?t.useQuery:void 0)||!1,this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)")}return T(r,e),r.prototype.onAuthStateChange=function(t,e){var o,i=this;return o=function(){return function(){return r.__super__.onAuthStateChange.call(i,t,e)}}(),this.setStorageKey(t),t.authState===n.RESET?this.loadCredentials(function(t){return t&&t.authState?t.token===i.locationToken()&&t.authState===n.REQUEST?(t.authState=n.AUTHORIZED,i.storeCredentials(t,o)):i.forgetCredentials(o):o()}):o()},r.prototype.url=function(){return this.receiverUrl},r.prototype.doAuthorize=function(t){return window.location.assign(t)},r.prototype.computeUrl=function(){var e,r,n,o;return o="_dropboxjs_scope="+encodeURIComponent(this.scope),r=t.Drivers.BrowserBase.currentLocation(),-1===r.indexOf("#")?e=null:(n=r.split("#",2),r=n[0],e=n[1]),this.useQuery?r+=-1===r.indexOf("?")?"?"+o:"&"+o:e="?"+o,e?r+"#"+e:r},r.prototype.locationToken=function(){var e,r,n;return e=t.Drivers.BrowserBase.currentLocation(),n="_dropboxjs_scope="+encodeURIComponent(this.scope)+"&",-1===("function"==typeof e.indexOf?e.indexOf(n):void 0)?null:(r=this.tokenRe.exec(e),r?decodeURIComponent(r[2]):null)},r}(t.Drivers.BrowserBase),t.Drivers.Popup=function(e){function r(t){r.__super__.constructor.call(this,t),this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)")}return T(r,e),r.prototype.onAuthStateChange=function(t,e){var o,i=this;return o=function(){return function(){return r.__super__.onAuthStateChange.call(i,t,e)}}(),this.setStorageKey(t),t.authState===n.RESET?this.loadCredentials(function(t){return t&&t.authState?i.forgetCredentials(o):o()}):o()},r.prototype.doAuthorize=function(t,e,r,n){return this.listenForMessage(e,n),this.openWindow(t)},r.prototype.url=function(){return this.receiverUrl},r.prototype.computeUrl=function(e){var r;if(e){if(e.receiverUrl)return e.noFragment||-1!==e.receiverUrl.indexOf("#")?e.receiverUrl:e.receiverUrl+"#";if(e.receiverFile)return r=t.Drivers.BrowserBase.currentLocation().split("/"),r[r.length-1]=e.receiverFile,e.noFragment?r.join("/"):r.join("/")+"#"}return t.Drivers.BrowserBase.currentLocation()},r.prototype.openWindow=function(t){return window.open(t,"_dropboxOauthSigninWindow",this.popupWindowSpec(980,700))},r.prototype.popupWindowSpec=function(t,e){var r,n,o,i,s,a,h,u,l,p;return s=null!=(h=window.screenX)?h:window.screenLeft,a=null!=(u=window.screenY)?u:window.screenTop,i=null!=(l=window.outerWidth)?l:document.documentElement.clientWidth,r=null!=(p=window.outerHeight)?p:document.documentElement.clientHeight,n=Math.round(s+(i-t)/2),o=Math.round(a+(r-e)/2.5),s>n&&(n=s),a>o&&(o=a),"width="+t+",height="+e+","+("left="+n+",top="+o)+"dialog=yes,dependent=yes,scrollbars=yes,location=yes"},r.prototype.listenForMessage=function(t,e){var r,n=this;return r=function(o){var i;return i=n.tokenRe.exec(""+o.data),i&&decodeURIComponent(i[2])===t?(window.removeEventListener("message",r),e()):void 0},window.addEventListener("message",r,!1)},r.oauthReceiver=function(){return window.addEventListener("load",function(){var t;if(t=window.opener,window.parent!==window.top&&(t||(t=window.parent)),t){try{t.postMessage(window.location.href,"*")}catch(e){}return window.close()}})},r}(t.Drivers.BrowserBase),e=null,r=null,"undefined"!=typeof chrome&&null!==chrome&&(chrome.runtime&&(chrome.runtime.onMessage&&(e=chrome.runtime.onMessage),chrome.runtime.sendMessage&&(r=function(t){return chrome.runtime.sendMessage(t)})),chrome.extension&&(chrome.extension.onMessage&&(e||(e=chrome.extension.onMessage)),chrome.extension.sendMessage&&(r||(r=function(t){return chrome.extension.sendMessage(t)}))),e||function(){var e,r;return r=function(e){return e.Dropbox?(t.Drivers.Chrome.prototype.onMessage=e.Dropbox.Drivers.Chrome.onMessage,t.Drivers.Chrome.prototype.sendMessage=e.Dropbox.Drivers.Chrome.sendMessage):(e.Dropbox=t,t.Drivers.Chrome.prototype.onMessage=new t.EventSource,t.Drivers.Chrome.prototype.sendMessage=function(e){return t.Drivers.Chrome.prototype.onMessage.dispatch(e)})},chrome.extension&&chrome.extension.getBackgroundPage&&(e=chrome.extension.getBackgroundPage())?r(e):chrome.runtime&&chrome.runtime.getBackgroundPage?chrome.runtime.getBackgroundPage(function(t){return r(t)}):void 0}()),t.Drivers.Chrome=function(){function n(t){var e,r;e=t&&t.receiverPath||"chrome_oauth_receiver.html",this.receiverUrl=this.expandUrl(e),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)"),r=t&&t.scope||"default",this.storageKey="dropbox_js_"+r+"_credentials"}return n.prototype.onMessage=e,n.prototype.sendMessage=r,n.prototype.expandUrl=function(t){return chrome.runtime&&chrome.runtime.getURL?chrome.runtime.getURL(t):chrome.extension&&chrome.extension.getURL?chrome.extension.getURL(t):t},n.prototype.onAuthStateChange=function(e,r){var n=this;switch(e.authState){case t.Client.RESET:return this.loadCredentials(function(t){if(t){if(t.authState)return n.forgetCredentials(r);e.setCredentials(t)}return r()});case t.Client.DONE:return this.storeCredentials(e.credentials(),r);case t.Client.SIGNED_OFF:return this.forgetCredentials(r);case t.Client.ERROR:return this.forgetCredentials(r);default:return r()}},n.prototype.doAuthorize=function(t,e,r,n){var o;return o={handle:null},this.listenForMessage(e,o,n),this.openWindow(t,function(t){return o.handle=t})},n.prototype.openWindow=function(t,e){return chrome.tabs&&chrome.tabs.create?(chrome.tabs.create({url:t,active:!0,pinned:!1},function(t){return e(t)}),this):chrome.app&&chrome.app.window&&chrome.app.window.create?(chrome.app.window.create(t,{frame:"none",id:"dropbox-auth"},function(t){return e(t)}),this):this},n.prototype.closeWindow=function(t){return chrome.tabs&&chrome.tabs.remove&&t.id?(chrome.tabs.remove(t.id),this):chrome.app&&chrome.app.window&&t.close?(t.close(),this):this},n.prototype.url=function(){return this.receiverUrl},n.prototype.listenForMessage=function(t,e,r){var n,o=this;return n=function(i,s){var a;if(!s||!s.tab||s.tab.url.substring(0,o.receiverUrl.length)===o.receiverUrl)return a=o.tokenRe.exec(i.dropbox_oauth_receiver_href||""),a&&decodeURIComponent(a[2])===t?(e.handle&&o.closeWindow(e.handle),o.onMessage.removeListener(n),r()):void 0},this.onMessage.addListener(n)},n.prototype.storeCredentials=function(t,e){var r;return r={},r[this.storageKey]=t,chrome.storage.local.set(r,e),this},n.prototype.loadCredentials=function(t){var e=this;return chrome.storage.local.get(this.storageKey,function(r){return t(r[e.storageKey]||null)}),this},n.prototype.forgetCredentials=function(t){return chrome.storage.local.remove(this.storageKey,t),this},n.oauthReceiver=function(){return window.addEventListener("load",function(){var e;return e=new t.Drivers.Chrome,e.sendMessage({dropbox_oauth_receiver_href:window.location.href}),window.close?window.close():void 0})},n}(),t.Drivers.NodeServer=function(){function t(t){this.port=(null!=t?t.port:void 0)||8912,this.faviconFile=(null!=t?t.favicon:void 0)||null,this.fs=require("fs"),this.http=require("http"),this.open=require("open"),this.callbacks={},this.urlRe=RegExp("^/oauth_callback\\?"),this.tokenRe=RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"),this.createApp()}return t.prototype.url=function(){return"http://localhost:"+this.port+"/oauth_callback"},t.prototype.doAuthorize=function(t,e,r,n){return this.callbacks[e]=n,this.openBrowser(t)},t.prototype.openBrowser=function(t){if(!t.match(/^https?:\/\//))throw Error("Not a http/https URL: "+t);return this.open(t)},t.prototype.createApp=function(){var t=this;return this.app=this.http.createServer(function(e,r){return t.doRequest(e,r)}),this.app.listen(this.port)},t.prototype.closeServer=function(){return this.app.close()},t.prototype.doRequest=function(t,e){var r,n,o,i=this;return this.urlRe.exec(t.url)&&(n=this.tokenRe.exec(t.url),n&&(o=decodeURIComponent(n[2]),this.callbacks[o]&&(this.callbacks[o](),delete this.callbacks[o]))),r="",t.on("data",function(t){return r+=t}),t.on("end",function(){return i.faviconFile&&"/favicon.ico"===t.url?i.sendFavicon(e):i.closeBrowser(e)})},t.prototype.closeBrowser=function(t){var e;return e='\n\nPlease close this window.
',t.writeHead(200,{"Content-Length":e.length,"Content-Type":"text/html"}),t.write(e),t.end},t.prototype.sendFavicon=function(t){return this.fs.readFile(this.faviconFile,function(e,r){return t.writeHead(200,{"Content-Length":r.length,"Content-Type":"image/x-icon"}),t.write(r),t.end})},t}(),t.EventSource=function(){function t(t){this._cancelable=t&&t.cancelable,this._listeners=[]}return t.prototype.addListener=function(t){if("function"!=typeof t)throw new TypeError("Invalid listener type; expected function");return 0>k.call(this._listeners,t)&&this._listeners.push(t),this},t.prototype.removeListener=function(t){var e,r,n,o,i,s;if(this._listeners.indexOf)r=this._listeners.indexOf(t),-1!==r&&this._listeners.splice(r,1);else for(s=this._listeners,e=o=0,i=s.length;i>o;e=++o)if(n=s[e],n===t){this._listeners.splice(e,1);break}return this},t.prototype.dispatch=function(t){var e,r,n,o,i;for(i=this._listeners,n=0,o=i.length;o>n;n++)if(e=i[n],r=e(t),this._cancelable&&r===!1)return!1;return!0},t}(),y=function(t,e){return p(b(C(t),C(e),t.length,e.length))},v=function(t){return p(E(C(t),t.length))},("undefined"==typeof window||null===window)&&(w=require("crypto"),y=function(t,e){var r;return r=w.createHmac("sha1",e),r.update(t),r.digest("base64")},v=function(t){var e;return e=w.createHash("sha1"),e.update(t),e.digest("base64")}),b=function(t,e,r,n){var o,i,s,a;return e.length>16&&(e=E(e,n)),s=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(909522486^e[i]);return r}(),a=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(1549556828^e[i]);return r}(),o=E(s.concat(t),64+r),E(a.concat(o),84)},E=function(t,e){var r,n,o,i,s,a,h,u,p,c,d,f,y,v,m,g,w,S;for(t[e>>2]|=1<<31-((3&e)<<3),t[(e+8>>6<<4)+15]=e<<3,g=Array(80),r=1732584193,o=-271733879,s=-1732584194,h=271733878,p=-1009589776,f=0,m=t.length;m>f;){for(n=r,i=o,a=s,u=h,c=p,y=S=0;80>S;y=++S)g[y]=16>y?t[f+y]:_(g[y-3]^g[y-8]^g[y-14]^g[y-16],1),20>y?(d=o&s|~o&h,v=1518500249):40>y?(d=o^s^h,v=1859775393):60>y?(d=o&s|o&h|s&h,v=-1894007588):(d=o^s^h,v=-899497514),w=l(l(_(r,5),d),l(l(p,g[y]),v)),p=h,h=s,s=_(o,30),o=r,r=w;r=l(r,n),o=l(o,i),s=l(s,a),h=l(h,u),p=l(p,c),f+=16}return[r,o,s,h,p]},_=function(t,e){return t<>>32-e},l=function(t,e){var r,n;return n=(65535&t)+(65535&e),r=(t>>16)+(e>>16)+(n>>16),r<<16|65535&n},p=function(t){var e,r,n,o,i;for(o="",e=0,n=4*t.length;n>e;)r=e,i=(255&t[r>>2]>>(3-(3&r)<<3))<<16,r+=1,i|=(255&t[r>>2]>>(3-(3&r)<<3))<<8,r+=1,i|=255&t[r>>2]>>(3-(3&r)<<3),o+=x[63&i>>18],o+=x[63&i>>12],e+=1,o+=e>=n?"=":x[63&i>>6],e+=1,o+=e>=n?"=":x[63&i],e+=1;return o},x="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",C=function(t){var e,r,n,o,i;for(e=[],n=255,r=o=0,i=t.length;i>=0?i>o:o>i;r=i>=0?++o:--o)e[r>>2]|=(t.charCodeAt(r)&n)<<(3-(3&r)<<3);return e},t.Oauth=function(){function e(t){this.key=this.k=null,this.secret=this.s=null,this.token=null,this.tokenSecret=null,this._appHash=null,this.reset(t)}return e.prototype.reset=function(t){var e,r,n,o;if(t.secret)this.k=this.key=t.key,this.s=this.secret=t.secret,this._appHash=null;else if(t.key)this.key=t.key,this.secret=null,n=c(S(this.key).split("|",2)[1]),o=n.split("?",2),e=o[0],r=o[1],this.k=decodeURIComponent(e),this.s=decodeURIComponent(r),this._appHash=null;else if(!this.k)throw Error("No API key supplied");return t.token?this.setToken(t.token,t.tokenSecret):this.setToken(null,"")},e.prototype.setToken=function(e,r){if(e&&!r)throw Error("No secret supplied with the user token");return this.token=e,this.tokenSecret=r||"",this.hmacKey=t.Xhr.urlEncodeValue(this.s)+"&"+t.Xhr.urlEncodeValue(r),null},e.prototype.authHeader=function(e,r,n){var o,i,s,a,h,u;this.addAuthParams(e,r,n),i=[];for(s in n)a=n[s],"oauth_"===s.substring(0,6)&&i.push(s);for(i.sort(),o=[],h=0,u=i.length;u>h;h++)s=i[h],o.push(t.Xhr.urlEncodeValue(s)+'="'+t.Xhr.urlEncodeValue(n[s])+'"'),delete n[s];return"OAuth "+o.join(",")},e.prototype.addAuthParams=function(t,e,r){return this.boilerplateParams(r),r.oauth_signature=this.signature(t,e,r),r},e.prototype.boilerplateParams=function(t){return t.oauth_consumer_key=this.k,t.oauth_nonce=this.nonce(),t.oauth_signature_method="HMAC-SHA1",this.token&&(t.oauth_token=this.token),t.oauth_timestamp=Math.floor(Date.now()/1e3),t.oauth_version="1.0",t},e.prototype.nonce=function(){return Date.now().toString(36)+Math.random().toString(36)},e.prototype.signature=function(e,r,n){var o;return o=e.toUpperCase()+"&"+t.Xhr.urlEncodeValue(r)+"&"+t.Xhr.urlEncodeValue(t.Xhr.urlEncode(n)),y(o,this.hmacKey)},e.prototype.appHash=function(){return this._appHash?this._appHash:this._appHash=v(this.k).replace(/\=/g,"")},e}(),null==Date.now&&(Date.now=function(){return(new Date).getTime()}),S=function(t,e){var r,n,o,i,s,a,h,u,l,p,d,f;for(e?(e=[encodeURIComponent(t),encodeURIComponent(e)].join("?"),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length/2;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(16*(15&t.charCodeAt(2*r))+(15&t.charCodeAt(2*r+1)));
+return o}()):(p=t.split("|",2),t=p[0],e=p[1],t=c(t),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}(),e=c(e)),i=function(){for(f=[],u=0;256>u;u++)f.push(u);return f}.apply(this),a=0,s=l=0;256>l;s=++l)a=(a+i[r]+t[s%t.length])%256,d=[i[a],i[s]],i[s]=d[0],i[a]=d[1];return s=a=0,o=function(){var t,r,o,u;for(u=[],h=t=0,r=e.length;r>=0?r>t:t>r;h=r>=0?++t:--t)s=(s+1)%256,a=(a+i[s])%256,o=[i[a],i[s]],i[s]=o[0],i[a]=o[1],n=i[(i[s]+i[a])%256],u.push(String.fromCharCode((n^e.charCodeAt(h))%256));return u}(),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(String.fromCharCode(t[r]));return o}(),[m(t.join("")),m(o.join(""))].join("|")},t.PulledChanges=function(){function e(e){var r;this.blankSlate=e.reset||!1,this.cursorTag=e.cursor,this.shouldPullAgain=e.has_more,this.shouldBackOff=!this.shouldPullAgain,this.changes=e.cursor&&e.cursor.length?function(){var n,o,i,s;for(i=e.entries,s=[],n=0,o=i.length;o>n;n++)r=i[n],s.push(t.PullChange.parse(r));return s}():[]}return e.parse=function(e){return e&&"object"==typeof e?new t.PulledChanges(e):e},e.prototype.blankSlate=void 0,e.prototype.cursorTag=void 0,e.prototype.changes=void 0,e.prototype.shouldPullAgain=void 0,e.prototype.shouldBackOff=void 0,e.prototype.cursor=function(){return this.cursorTag},e}(),t.PullChange=function(){function e(e){this.path=e[0],this.stat=t.Stat.parse(e[1]),this.stat?this.wasRemoved=!1:(this.stat=null,this.wasRemoved=!0)}return e.parse=function(e){return e&&"object"==typeof e?new t.PullChange(e):e},e.prototype.path=void 0,e.prototype.wasRemoved=void 0,e.prototype.stat=void 0,e}(),t.PublicUrl=function(){function e(t,e){this.url=t.url,this.expiresAt=new Date(Date.parse(t.expires)),this.isDirect=e===!0?!0:e===!1?!1:"direct"in t?t.direct:864e5>=Date.now()-this.expiresAt,this.isPreview=!this.isDirect,this._json=null}return e.parse=function(e,r){return e&&"object"==typeof e?new t.PublicUrl(e,r):e},e.prototype.url=null,e.prototype.expiresAt=null,e.prototype.isDirect=null,e.prototype.isPreview=null,e.prototype.json=function(){return this._json||(this._json={url:this.url,expires:""+this.expiresAt,direct:this.isDirect})},e}(),t.CopyReference=function(){function e(t){"object"==typeof t?(this.tag=t.copy_ref,this.expiresAt=new Date(Date.parse(t.expires)),this._json=t):(this.tag=t,this.expiresAt=new Date(1e3*Math.ceil(Date.now()/1e3)),this._json=null)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.CopyReference(e)},e.prototype.tag=null,e.prototype.expiresAt=null,e.prototype.json=function(){return this._json||(this._json={copy_ref:this.tag,expires:""+this.expiresAt})},e}(),t.Stat=function(){function e(t){var e,r,n,o;switch(this._json=t,this.path=t.path,"/"!==this.path.substring(0,1)&&(this.path="/"+this.path),e=this.path.length-1,e>=0&&"/"===this.path.substring(e)&&(this.path=this.path.substring(0,e)),r=this.path.lastIndexOf("/"),this.name=this.path.substring(r+1),this.isFolder=t.is_dir||!1,this.isFile=!this.isFolder,this.isRemoved=t.is_deleted||!1,this.typeIcon=t.icon,this.modifiedAt=(null!=(n=t.modified)?n.length:void 0)?new Date(Date.parse(t.modified)):null,this.clientModifiedAt=(null!=(o=t.client_mtime)?o.length:void 0)?new Date(Date.parse(t.client_mtime)):null,t.root){case"dropbox":this.inAppFolder=!1;break;case"app_folder":this.inAppFolder=!0;break;default:this.inAppFolder=null}this.size=t.bytes||0,this.humanSize=t.size||"",this.hasThumbnail=t.thumb_exists||!1,this.isFolder?(this.versionTag=t.hash,this.mimeType=t.mime_type||"inode/directory"):(this.versionTag=t.rev,this.mimeType=t.mime_type||"application/octet-stream")}return e.parse=function(e){return e&&"object"==typeof e?new t.Stat(e):e},e.prototype.path=null,e.prototype.name=null,e.prototype.inAppFolder=null,e.prototype.isFolder=null,e.prototype.isFile=null,e.prototype.isRemoved=null,e.prototype.typeIcon=null,e.prototype.versionTag=null,e.prototype.mimeType=null,e.prototype.size=null,e.prototype.humanSize=null,e.prototype.hasThumbnail=null,e.prototype.modifiedAt=null,e.prototype.clientModifiedAt=null,e.prototype.json=function(){return this._json},e}(),t.UploadCursor=function(){function e(t){this.replace(t)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.UploadCursor(e)},e.prototype.tag=null,e.prototype.offset=null,e.prototype.expiresAt=null,e.prototype.json=function(){return this._json||(this._json={upload_id:this.tag,offset:this.offset,expires:""+this.expiresAt})},e.prototype.replace=function(t){return"object"==typeof t?(this.tag=t.upload_id||null,this.offset=t.offset||0,this.expiresAt=new Date(Date.parse(t.expires)||Date.now()),this._json=t):(this.tag=t||null,this.offset=0,this.expiresAt=new Date(1e3*Math.floor(Date.now()/1e3)),this._json=null),this},e}(),t.UserInfo=function(){function e(t){var e;this._json=t,this.name=t.display_name,this.email=t.email,this.countryCode=t.country||null,this.uid=""+t.uid,t.public_app_url?(this.publicAppUrl=t.public_app_url,e=this.publicAppUrl.length-1,e>=0&&"/"===this.publicAppUrl.substring(e)&&(this.publicAppUrl=this.publicAppUrl.substring(0,e))):this.publicAppUrl=null,this.referralUrl=t.referral_link,this.quota=t.quota_info.quota,this.privateBytes=t.quota_info.normal||0,this.sharedBytes=t.quota_info.shared||0,this.usedQuota=this.privateBytes+this.sharedBytes}return e.parse=function(e){return e&&"object"==typeof e?new t.UserInfo(e):e},e.prototype.name=null,e.prototype.email=null,e.prototype.countryCode=null,e.prototype.uid=null,e.prototype.referralUrl=null,e.prototype.publicAppUrl=null,e.prototype.quota=null,e.prototype.usedQuota=null,e.prototype.privateBytes=null,e.prototype.sharedBytes=null,e.prototype.json=function(){return this._json},e}(),"undefined"!=typeof window&&null!==window?(!window.XDomainRequest||"withCredentials"in new XMLHttpRequest?(h=window.XMLHttpRequest,a=!1,i=-1===window.navigator.userAgent.indexOf("Firefox")):(h=window.XDomainRequest,a=!0,i=!1),s=!0):(h=require("xmlhttprequest").XMLHttpRequest,a=!1,i=!1,s=!1),"undefined"==typeof Uint8Array?(o=null,u=!1):(Object.getPrototypeOf?o=Object.getPrototypeOf(Object.getPrototypeOf(new Uint8Array(0))).constructor:Object.__proto__&&(o=new Uint8Array(0).__proto__.__proto__.constructor),u=o!==Object),t.Xhr=function(){function e(t,e){this.method=t,this.isGet="GET"===this.method,this.url=e,this.headers={},this.params=null,this.body=null,this.preflight=!(this.isGet||"POST"===this.method),this.signed=!1,this.responseType=null,this.callback=null,this.xhr=null,this.onError=null}return e.Request=h,e.ieXdr=a,e.canSendForms=i,e.doesPreflight=s,e.ArrayBufferView=o,e.sendArrayBufferView=u,e.prototype.xhr=null,e.prototype.onError=null,e.prototype.setParams=function(t){if(this.signed)throw Error("setParams called after addOauthParams or addOauthHeader");if(this.params)throw Error("setParams cannot be called twice");return this.params=t,this},e.prototype.setCallback=function(t){return this.callback=t,this},e.prototype.signWithOauth=function(e,r){return t.Xhr.ieXdr?this.addOauthParams(e):this.preflight||!t.Xhr.doesPreflight?this.addOauthHeader(e):this.isGet&&r?this.addOauthHeader(e):this.addOauthParams(e)},e.prototype.addOauthParams=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),t.addAuthParams(this.method,this.url,this.params),this.signed=!0,this},e.prototype.addOauthHeader=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),this.signed=!0,this.setHeader("Authorization",t.authHeader(this.method,this.url,this.params))},e.prototype.setBody=function(t){if(this.isGet)throw Error("setBody cannot be called on GET requests");if(null!==this.body)throw Error("Request already has a body");return"string"==typeof t||"undefined"!=typeof FormData&&t instanceof FormData||(this.headers["Content-Type"]="application/octet-stream",this.preflight=!0),this.body=t,this},e.prototype.setResponseType=function(t){return this.responseType=t,this},e.prototype.setHeader=function(t,e){var r;if(this.headers[t])throw r=this.headers[t],Error("HTTP header "+t+" already set to "+r);if("Content-Type"===t)throw Error("Content-Type is automatically computed based on setBody");return this.preflight=!0,this.headers[t]=e,this},e.prototype.setFileField=function(e,r,n,o){var i,s;if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("setFileField cannot be called on GET requests");return"object"==typeof n&&"undefined"!=typeof Blob?("undefined"!=typeof ArrayBuffer&&(n instanceof ArrayBuffer?t.Xhr.sendArrayBufferView&&(n=new Uint8Array(n)):!t.Xhr.sendArrayBufferView&&0===n.byteOffset&&n.buffer instanceof ArrayBuffer&&(n=n.buffer)),o||(o="application/octet-stream"),n=new Blob([n],{type:o}),"undefined"!=typeof File&&n instanceof File&&(n=new Blob([n],{type:n.type})),s=n instanceof Blob):s=!1,s?(this.body=new FormData,this.body.append(e,n,r)):(o||(o="application/octet-stream"),i=this.multipartBoundary(),this.headers["Content-Type"]="multipart/form-data; boundary="+i,this.body=["--",i,"\r\n",'Content-Disposition: form-data; name="',e,'"; filename="',r,'"\r\n',"Content-Type: ",o,"\r\n","Content-Transfer-Encoding: binary\r\n\r\n",n,"\r\n","--",i,"--","\r\n"].join(""))},e.prototype.multipartBoundary=function(){return[Date.now().toString(36),Math.random().toString(36)].join("----")},e.prototype.paramsToUrl=function(){var e;return this.params&&(e=t.Xhr.urlEncode(this.params),0!==e.length&&(this.url=[this.url,"?",e].join("")),this.params=null),this},e.prototype.paramsToBody=function(){if(this.params){if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");this.headers["Content-Type"]="application/x-www-form-urlencoded",this.body=t.Xhr.urlEncode(this.params),this.params=null}return this},e.prototype.prepare=function(){var e,r,n,o,i=this;if(r=t.Xhr.ieXdr,this.isGet||null!==this.body||r?(this.paramsToUrl(),null!==this.body&&"string"==typeof this.body&&(this.headers["Content-Type"]="text/plain; charset=utf8")):this.paramsToBody(),this.xhr=new t.Xhr.Request,r?(this.xhr.onload=function(){return i.onXdrLoad()},this.xhr.onerror=function(){return i.onXdrError()},this.xhr.ontimeout=function(){return i.onXdrError()},this.xhr.onprogress=function(){}):this.xhr.onreadystatechange=function(){return i.onReadyStateChange()},this.xhr.open(this.method,this.url,!0),!r){o=this.headers;for(e in o)R.call(o,e)&&(n=o[e],this.xhr.setRequestHeader(e,n))}return this.responseType&&("b"===this.responseType?this.xhr.overrideMimeType&&this.xhr.overrideMimeType("text/plain; charset=x-user-defined"):this.xhr.responseType=this.responseType),this},e.prototype.send=function(e){var r;if(this.callback=e||this.callback,null!==this.body){r=this.body,t.Xhr.sendArrayBufferView&&r instanceof ArrayBuffer&&(r=new Uint8Array(r));try{this.xhr.send(r)}catch(n){if(t.Xhr.sendArrayBufferView||"undefined"==typeof Blob)throw n;r=new Blob([r],{type:"application/octet-stream"}),this.xhr.send(r)}}else this.xhr.send();return this},e.urlEncode=function(t){var e,r,n;e=[];for(r in t)n=t[r],e.push(this.urlEncodeValue(r)+"="+this.urlEncodeValue(n));return e.sort().join("&")},e.urlEncodeValue=function(t){return encodeURIComponent(""+t).replace(/\!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")},e.urlDecode=function(t){var e,r,n,o,i,s;for(r={},s=t.split("&"),o=0,i=s.length;i>o;o++)n=s[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r},e.prototype.onReadyStateChange=function(){var e,r,n,o,i,s,a,h,u;if(4!==this.xhr.readyState)return!0;if(200>this.xhr.status||this.xhr.status>=300)return e=new t.ApiError(this.xhr,this.method,this.url),this.onError&&this.onError.dispatch(e),this.callback(e),!0;if(s=this.xhr.getResponseHeader("x-dropbox-metadata"),null!=s?s.length:void 0)try{i=JSON.parse(s)}catch(l){i=void 0}else i=void 0;if(this.responseType){if("b"===this.responseType){for(n=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,r=[],o=h=0,u=n.length;u>=0?u>h:h>u;o=u>=0?++h:--h)r.push(String.fromCharCode(255&n.charCodeAt(o)));a=r.join(""),this.callback(null,a,i)}else this.callback(null,this.xhr.response,i);return!0}switch(a=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,this.xhr.getResponseHeader("Content-Type")){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(a),i);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(a),i);break;default:this.callback(null,a,i)}return!0},e.prototype.onXdrLoad=function(){var e;switch(e=this.xhr.responseText,this.xhr.contentType){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(e),void 0);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(e),void 0);break;default:this.callback(null,e,void 0)}return!0},e.prototype.onXdrError=function(){var e;return e=new t.ApiError(this.xhr,this.method,this.url),this.onError&&this.onError.dispatch(e),this.callback(e),!0},e}(),null!=("undefined"!=typeof module&&null!==module?module.exports:void 0))module.exports=t;else{if("undefined"==typeof window||null===window)throw Error("This library only supports node.js and modern browsers.");window.Dropbox=t}t.atob=c,t.btoa=m,t.hmac=y,t.sha1=v,t.encodeKey=S}).call(this);
\ No newline at end of file
diff --git a/lib/client/storage/dropbox/package.json b/lib/client/storage/dropbox/package.json
index dfa72764..553f5fa7 100644
--- a/lib/client/storage/dropbox/package.json
+++ b/lib/client/storage/dropbox/package.json
@@ -1,6 +1,6 @@
{
"name": "dropbox",
- "version": "0.7.2",
+ "version": "0.8.1",
"description": "Client library for the Dropbox API",
"keywords": ["dropbox", "filesystem", "storage"],
"homepage": "http://github.com/dropbox/dropbox-js",
@@ -23,14 +23,16 @@
"devDependencies": {
"async": ">= 0.1.22",
"chai": ">= 1.4.0",
- "codo": ">= 1.5.2",
+ "codo": ">= 1.5.4",
"coffee-script": ">= 1.4.0",
- "express": ">= 3.0.4",
+ "express": ">= 3.0.6",
+ "glob": ">= 3.1.14",
"mocha": ">= 1.7.4",
+ "open": "https://github.com/pwnall/node-open/tarball/master",
"remove": ">= 0.1.5",
"sinon": ">= 1.5.2",
- "sinon-chai": ">= 2.2.0",
- "uglify-js": ">= 2.2.2"
+ "sinon-chai": ">= 2.3.0",
+ "uglify-js": ">= 2.2.3"
},
"main": "lib/dropbox.js",
"directories": {
@@ -40,7 +42,7 @@
"test": "test"
},
"scripts": {
- "prepublish": "cake build",
- "test": "cake test"
+ "prepublish": "node_modules/coffee-script/bin/cake build",
+ "test": "node_modules/coffee-script/bin/cake test"
}
}
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/README.md b/lib/client/storage/dropbox/samples/checkbox.js/README.md
new file mode 100644
index 00000000..dc593a05
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/README.md
@@ -0,0 +1,67 @@
+# Checkbox, a dropbox.js Sample Application
+
+This application demonstrates the use of the JavaScript client library for the
+Dropbox API to implement a Dropbox-backed To Do list application.
+
+In 70 lines of HTML, and 250 lines of commented CoffeeScript, Checkbox lets you
+store your To Do list in your Dropbox! Just don't expect award winning design
+or usability from a sample application.
+
+See this sample in action
+[here](https://dl-web.dropbox.com/spa/pjlfdak1tmznswp/checkbox.js/public/index.html).
+
+
+## Dropbox Integration
+
+This proof-of-concept application uses the "App folder" Dropbox access level,
+so Dropbox automatically creates a directory for its app data in the users'
+Dropboxes. The data model optimizes for ease of development and debugging.
+Each task is stored as a file whose name is the task’s description. Tasks are
+grouped under two folders, active and done.
+
+The main advantage of this data model is that operations on tasks cleanly map
+to file operations in Dropbox. At initialization time, the application creates
+its two folders, active and done. A task is created by writing an empty string
+to a file in the active folder, marked as completed by moving the file to the
+done folder, and removed by deleting the associated file.
+
+The lists of tasks are obtained by listing the contents of the active and done
+folders. The data model can be easily extended, by storing JSON-encoded
+information, such as deadlines, in the task files.
+
+This sample uses the following `Dropbox.Client` methods:
+
+* authenticate
+* signOff
+* getUserInfo
+* mkdir
+* readdir
+* writeFile
+* move
+* remove
+
+
+## Building
+
+This sample does not require building. Follow the steps below to get your own
+copy of the sample that you can hack on.
+
+1. [Create a powered_by.js app in your Dropbox](https://dl-web.dropbox.com/spa/pjlfdak1tmznswp/powered_by.js/public/index.html).
+1. [Get your own API key](https://www.dropbox.com/developers/apps).
+1. [Encode your API key](https://dl-web.dropbox.com/spa/pjlfdak1tmznswp/api_keys.js/public/index.html).
+1. Copy the source code to `/Apps/Static Web Apps/powered_by.js` in your Dropbox
+
+## Dependencies
+
+The application uses the following JavaScript libraries.
+
+* [dropbox.js](https://github.com/dropbox/dropbox-js) for Dropbox integration
+* [less](http://lesscss.org/) for CSS conciseness
+* [CoffeeScript](http://coffeescript.org/) for JavaScript conciseness
+* [jQuery](http://jquery.com/) for cross-browser compatibitility
+
+The icons used in the application are all from
+[the noun project](http://thenounproject.com/).
+
+The application follows a good practice of packaging its dependencies, and not
+hot-linking them.
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/checkbox.coffee b/lib/client/storage/dropbox/samples/checkbox.js/public/checkbox.coffee
new file mode 100644
index 00000000..5d68085b
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/public/checkbox.coffee
@@ -0,0 +1,249 @@
+# vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab :
+
+# Controller/View for the application.
+class Checkbox
+ # @param {Dropbox.Client} dbClient a non-authenticated Dropbox client
+ # @param {DOMElement} root the app's main UI element
+ constructor: (@dbClient, root) ->
+ @$root = $ root
+ @taskTemplate = $('#task-template').text()
+ @$activeList = $('#active-task-list')
+ @$doneList = $('#done-task-list')
+ $('#signout-button').click (event) => @onSignOut event
+
+ @dbClient.authenticate (error, data) =>
+ return @showError(error) if error
+ @dbClient.getUserInfo (error, userInfo) =>
+ return @showError(error) if error
+ $('#user-name').text userInfo.name
+ @tasks = new Tasks @, @dbClient
+ @tasks.load =>
+ @wire()
+ @render()
+ @$root.removeClass 'hidden'
+
+ # Re-renders all the data.
+ render: ->
+ @$activeList.empty()
+ @$doneList.empty()
+ @renderTask(task) for task in @tasks.active
+ @renderTask(task) for task in @tasks.done
+
+ # Renders a task into the
+ renderTask: (task) ->
+ $list = if task.done then @$doneList else @$activeList
+ $list.append @$taskDom(task)
+
+ # Renders the list element representing a task.
+ #
+ # @param {Task} task the task to be rendered
+ # @return {jQuery} jQuery wrapper for the DOM representing the task
+ $taskDom: (task) ->
+ $task = $ @taskTemplate
+ $('.task-name', $task).text task.name
+ $('.task-remove-button', $task).click (event) => @onRemoveTask event, task
+ if task.done
+ $('.task-done-button', $task).addClass 'hidden'
+ $('.task-active-button', $task).click (event) =>
+ @onActiveTask event, task
+ else
+ $('.task-active-button', $task).addClass 'hidden'
+ $('.task-done-button', $task).click (event) => @onDoneTask event, task
+ $task
+
+ # Called when the user wants to create a new task.
+ onNewTask: (event) ->
+ event.preventDefault()
+ name = $('#new-task-name').val()
+ if @tasks.findByName name
+ alert "You already have this task on your list!"
+ else
+ $('#new-task-button').attr 'disabled', 'disabled'
+ $('#new-task-name').attr 'disabled', 'disabled'
+ task = new Task()
+ task.name = name
+ @tasks.addTask task, =>
+ $('#new-task-name').removeAttr('disabled').val ''
+ $('#new-task-button').removeAttr 'disabled'
+ @renderTask task
+
+ # Called when the user wants to mark a task as done.
+ onDoneTask: (event, task) ->
+ $task = @$taskElement event.target
+ $('button', $task).attr 'disabled', 'disabled'
+ @tasks.setTaskDone task, true, =>
+ $task.remove()
+ @renderTask task
+
+ # Called when the user wants to mark a task as active.
+ onActiveTask: (event, task) ->
+ $task = @$taskElement event.target
+ $('button', $task).attr 'disabled', 'disabled'
+ @tasks.setTaskDone task, false, =>
+ $task.remove()
+ @renderTask task
+
+ # Called when the user wants to permanently remove a task.
+ onRemoveTask: (event, task) ->
+ $task = @$taskElement event.target
+ $('button', $task).attr 'disabled', 'disabled'
+ @tasks.removeTask task, ->
+ $task.remove()
+
+ # Called when the user wants to sign out of the application.
+ onSignOut: (event, task) ->
+ @dbClient.signOut (error) =>
+ return @showError(error) if error
+ window.location.reload()
+
+ # Finds the DOM element representing a task.
+ #
+ # @param {DOMElement} element any element inside the task element
+ # @return {jQuery} a jQuery wrapper around the DOM element
+ # representing a task
+ $taskElement: (element) ->
+ $(element).closest 'li.task'
+
+ # Sets up listeners for the relevant DOM events.
+ wire: ->
+ $('#new-task-form').submit (event) => @onNewTask event
+
+ # Updates the UI to show that an error has occurred.
+ showError: (error) ->
+ $('#error-notice').removeClass 'hidden'
+ console.log error if window.console
+
+# Model that wraps all a user's tasks.
+class Tasks
+ # @param {Checkbox} controller the application controller
+ constructor: (@controller) ->
+ @dbClient = @controller.dbClient
+ [@active, @done] = [[], []]
+
+ # Reads all the from a user's Dropbox.
+ #
+ # @param {function()} done called when all the tasks are read from the user's
+ # Dropbox, and the active and done properties are set
+ load: (done) ->
+ # We read the done tasks and the active tasks in parallel. The variables
+ # below tell us when we're done with both.
+ readActive = readDone = false
+
+ @dbClient.mkdir '/active', (error, stat) =>
+ # Issued mkdir so we always have a directory to read from.
+ # In most cases, this will fail, so don't bother checking for errors.
+ @dbClient.readdir '/active', (error, entries, dir_stat, entry_stats) =>
+ return @showError(error) if error
+ @active = ((new Task()).fromStat(stat) for stat in entry_stats)
+ readActive = true
+ done() if readActive and readDone
+ @dbClient.mkdir '/done', (error, stat) =>
+ @dbClient.readdir '/done', (error, entries, dir_stat, entry_stats) =>
+ return @showError(error) if error
+ @done = ((new Task()).fromStat(stat) for stat in entry_stats)
+ readDone = true
+ done() if readActive and readDone
+ @
+
+ # Adds a new task to the user's set of tasks.
+ #
+ # @param {Task} task the task to be added
+ # @param {function()} done called when the task is saved to the user's
+ # Dropbox
+ addTask: (task, done) ->
+ task.cleanupName()
+ @dbClient.writeFile task.path(), '', (error, stat) =>
+ return @showError(error) if error
+ @addTaskToModel task
+ done()
+
+ # Returns a task with the given name, if it exists.
+ #
+ # @param {String} name the name to search for
+ # @return {?Task} task the task with the given name, or null if no such task
+ # exists
+ findByName: (name) ->
+ for tasks in [@active, @done]
+ for task in tasks
+ return task if task.name is name
+ null
+
+ # Removes a task from the list of tasks.
+ #
+ # @param {Task} task the task to be removed
+ # @param {function()} done called when the task is removed from the user's
+ # Dropbox
+ removeTask: (task, done) ->
+ @dbClient.remove task.path(), (error, stat) =>
+ return @showError(error) if error
+ @removeTaskFromModel task
+ done()
+
+ # Marks a active task as done, or a done task as active.
+ #
+ # @param {Task} the task to be changed
+ setTaskDone: (task, newDoneValue, done) ->
+ [oldDoneValue, task.done] = [task.done, newDoneValue]
+ newPath = task.path()
+ task.done = oldDoneValue
+
+ @dbClient.move task.path(), newPath, (error, stat) =>
+ return @showError(error) if error
+ @removeTaskFromModel task
+ task.done = newDoneValue
+ @addTaskToModel task
+ done()
+
+ # Adds a task to the in-memory model. Should not be called directly.
+ addTaskToModel: (task) ->
+ @taskArray(task).push task
+
+ # Remove a task from the in-memory model. Should not be called directly.
+ removeTaskFromModel: (task) ->
+ taskArray = @taskArray task
+ for _task, index in taskArray
+ if _task is task
+ taskArray.splice index, 1
+ break
+
+ # @param {Task} the task whose containing array should be returned
+ # @return {Array} the array that should contain the given task
+ taskArray: (task) ->
+ if task.done then @done else @active
+
+ # Updates the UI to show that an error has occurred.
+ showError: (error) ->
+ @controller.showError error
+
+# Model for a single user task.
+class Task
+ # Creates a task with default values.
+ constructor: ->
+ @name = null
+ @done = false
+
+ # Reads data about a task from the stat of is file in a user's Dropbox.
+ #
+ # @param {Dropbox.Stat} entry the directory entry representing the task
+ fromStat: (entry) ->
+ @name = entry.name
+ @done = entry.path.split('/', 3)[1] is 'done'
+ @
+
+ # Cleans up the task name so that it's valid Dropbox file name.
+ cleanupName: (name) ->
+ # English-only hack that removes slashes from the task name.
+ @name = @name.replace(/\ \/\ /g, ' or ').replace(/\//g, ' or ')
+ @
+
+ # Path to the file representing the task in the user's Dropbox.
+ # @return {String} fully-qualified path
+ path: ->
+ (if @done then '/done/' else '/active/') + @name
+
+# Start up the code when the DOM is fully loaded.
+$ ->
+ client = new Dropbox.Client(
+ key: '/Fahm0FLioA|ZxKxLxy5irfHqsCRs+Ceo8bwJjVPu8xZlfjgGzeCjQ', sandbox: true)
+ client.authDriver new Dropbox.Drivers.Redirect(rememberUser: true)
+ new Checkbox client, '#app-ui'
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/checkbox.less b/lib/client/storage/dropbox/samples/checkbox.js/public/checkbox.less
new file mode 100644
index 00000000..8f562f97
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/public/checkbox.less
@@ -0,0 +1,298 @@
+// vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab :
+
+.linear-gradient (@top: #ffffff, @bottom: #000000) {
+ background: @top;
+ filter: ~"progid:DXImageTransform.Microsoft.gradient(startColorstr='@{top}', endColorstr='@{bottom}')";
+ background: -webkit-linear-gradient(top, @top 0%, @bottom 100%);
+ background: -moz-linear-gradient(top, @top 0%, @bottom 100%);
+ background: -ms-linear-gradient(top, @top 0%, @bottom 100%);
+ background: linear-gradient(to bottom, @top 0%, @bottom 100%);
+}
+.border-radius (@radius) {
+ -webkit-border-radius: @radius;
+ -moz-border-radius: @radius;
+ border-radius: @radius;
+}
+.box-sizing (@sizing) {
+ -webkit-box-sizing: @sizing;
+ -moz-box-sizing: @sizing;
+ box-sizing: @sizing;
+}
+.box-shadow (@offset-x, @offset-y, @blur, @spread, @color) {
+ -webkit-box-shadow: @offset-x @offset-y @blur @spread @color;
+ -moz-box-shadow: @offset-x @offset-y @blur @spread @color;
+ box-shadow: @offset-x @offset-y @blur @spread @color;
+}
+
+.font-verdana () {
+ font-family: Verdana, Geneva, sans-serif;
+}
+.font-helvetica () {
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+html, body {
+ .linear-gradient (#ffffff, #f2f7fc);
+ background-attachment: fixed;
+
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+#error-notice {
+ display: block;
+ position: absolute;
+ width: 100%;
+ .box-sizing(border-box);
+
+ color: hsl(0, 75%, 33%);
+ font-size: 12pt;
+ .font-verdana();
+ line-height: 1.5;
+ text-align: center;
+ margin: 0;
+ padding: 0.2em 0 0.1em 0;
+
+ #error-refresh-button {
+ margin: 0 1em;
+ padding: 0 1em;
+ }
+
+ &.hidden {
+ display: none;
+ }
+}
+
+#app-ui {
+ width: 700px;
+ margin: 0 auto;
+
+ h1 {
+ .font-verdana();
+ font-size: 24px;
+ font-weight: 300;
+ line-height: 2;
+ text-transform: lowercase;
+ color: #bfbfbf;
+ margin: 0;
+ padding: 2em 0 0.25em 0;
+
+ small {
+ .font-verdana();
+ font-size: 10px;
+ font-weight: 400;
+ text-transform: uppercase;
+ color: #888888;
+
+ a {
+ font-style: normal;
+ color: #649cd1;
+ text-decoration: none;
+ }
+ }
+ }
+
+ #notebook-page {
+ border: 1px solid #aaaaaa;
+ -webkit-border-top-left-radius: 8px;
+ -webkit-border-top-right-radius: 8px;
+ -moz-border-radius-topleft: 8px;
+ -moz-border-radius-topright: 8px;
+ border-top-left-radius: 8px;
+ border-top-right-radius: 8px;
+
+ .box-shadow(0, 0, 10px, 5px, rgba(0, 0, 0, 0.05));
+
+ background-color: #fffddb;
+ margin: 0;
+ padding: 24px 0 60px 0;
+ }
+ #notebook-page, .task {
+ background-image: -webkit-linear-gradient(top, #f3aaaa 0%, #f3aaaa 100%),
+ -webkit-linear-gradient(top, #f3aaaa 0%, #f3aaaa 100%);
+ background-image: -moz-linear-gradient(top, #f3aaaa 0%, #f3aaaa 100%),
+ -moz-linear-gradient(top, #f3aaaa 0%, #f3aaaa 100%);
+ background-image: -ms-linear-gradient(top, #f3aaaa 0%, #f3aaaa 100%),
+ -ms-linear-gradient(top, #f3aaaa 0%, #f3aaaa 100%);
+ background-image: linear-gradient(to bottom, #f3aaaa 0%, #f3aaaa 100%),
+ linear-gradient(to bottom, #f3aaaa 0%, #f3aaaa 100%);
+ background-position: 70px 0px, 76px 0px;
+ background-size: 1px 100%, 1px 100%;
+ background-repeat: no-repeat, no-repeat;
+ }
+
+ #user-info {
+ margin: 0;
+ padding: 0 16px 8px 88px;
+
+ color: #555555;
+ .font-helvetica();
+ font-size: 16px;
+ line-height: 32px;
+ font-weight: 200;
+ text-align: right;
+
+ #user-name {
+ display: inline-block;
+ padding: 0 0 0 8px;
+ }
+
+ #signout-button {
+ visibility: hidden;
+ }
+ &:hover {
+ color: #000000;
+
+ #signout-button {
+ visibility: visible;
+ }
+ }
+ }
+
+ #new-task-form, h2, .task, .empty-task {
+ border-bottom: 1px solid #c9e4f2;
+ .font-helvetica();
+ font-size: 18px;
+ font-weight: 300;
+ line-height: 36px;
+ color: #555555;
+
+ margin: 0;
+ padding: 6px 10px 6px 88px;
+ }
+
+ #new-task-form {
+ margin: 0;
+
+ #new-task-name {
+ font-family: inherit;
+ font-size: inherit;
+
+ background: rgb(255, 254, 236);
+ }
+ .task-actions {
+ visibility: visible;
+ }
+ }
+ h2 {
+ font-size: 20px;
+ font-weight: 400;
+ color: #000000;
+ }
+ .task-list {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+ .task {
+ span.task-name {
+ display: inline-block;
+ }
+ &:hover {
+ background-color: #fffcaf;
+ .task-actions {
+ visibility: visible;
+ }
+ }
+ }
+ .task-name {
+ width: 373px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ vertical-align: middle;
+ }
+ .task-actions {
+ width: 220px;
+ display: inline-block;
+ vertical-align: middle;
+ line-height: 1;
+ visibility: hidden;
+ }
+}
+
+input[type=text] {
+ display: inline-block;
+ margin: 0;
+ padding: 0 0.25em 0 0.25em;
+ line-height: 30px;
+
+ .box-sizing(border-box);
+ position: relative;
+ left: -4px;
+ height: 32px;
+
+ border: 1px solid #d2cd70;
+ .border-radius(4px);
+ -webkit-box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.1);
+}
+
+button {
+ display: inline-block;
+ vertical-align: middle;
+ height: 32px;
+ margin: 0;
+ padding: 0;
+ min-width: 104px;
+ cursor: pointer;
+
+ border: 1px solid;
+ .border-radius(4px);
+ .box-shadow(0, 2px, -1px, 0, rgba(0, 0, 0, 0.1));
+
+ .font-verdana();
+ font-size: 13px;
+ font-weight: 400;
+ text-align: center;
+ text-transform: uppercase;
+
+ color: #ffffff;
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
+ filter: dropshadow(color=rgba(0, 0, 0, 0.5), offx=1, offy=1);
+
+ &:hover {
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
+ inset 0 0 4px rgba(255, 255, 255, 0.6);
+ -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
+ inset 0 0 4px rgba(255, 255, 255, 0.6);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
+ inset 0 0 4px rgba(255, 255, 255, 0.6);
+ }
+ &:focus {
+ .box-shadow(0, 0, 3px, 1px, #33a0e8);
+ }
+ &:active {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ }
+
+ img {
+ display: inline-block;
+ vertical-align: -4px;
+ }
+
+ &.task-done-button, new-task-button, &.task-active-button {
+ border-color: #448c42;
+ .linear-gradient(#8ed66b, #58ba6d);
+
+ &:active {
+ .linear-gradient(#58ba6d, #8ed66b);
+ }
+ }
+
+ &.task-remove-button, error-refresh-button, signout-button {
+ border-color: #a73030;
+ .linear-gradient(#f67f73, #bb5757);
+ &:active {
+ .linear-gradient(#bb5757, #f67f73);
+ }
+ }
+}
+
+.hidden {
+ display: none;
+}
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/add.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/add.png
new file mode 100644
index 00000000..4566ad47
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/add.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/done.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/done.png
new file mode 100644
index 00000000..69e0921b
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/done.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon.svg b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon.svg
new file mode 100644
index 00000000..46b4e9bd
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon.svg
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon128.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon128.png
new file mode 100644
index 00000000..f62d073f
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon128.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon16.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon16.png
new file mode 100644
index 00000000..09c902f4
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon16.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon64.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon64.png
new file mode 100644
index 00000000..f6f2c837
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/icon64.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/not_done.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/not_done.png
new file mode 100644
index 00000000..83c7ebdd
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/not_done.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/images/remove.png b/lib/client/storage/dropbox/samples/checkbox.js/public/images/remove.png
new file mode 100644
index 00000000..908b0d36
Binary files /dev/null and b/lib/client/storage/dropbox/samples/checkbox.js/public/images/remove.png differ
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/index.html b/lib/client/storage/dropbox/samples/checkbox.js/public/index.html
new file mode 100644
index 00000000..4f86ceea
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/public/index.html
@@ -0,0 +1,68 @@
+
+
+
+
+ Checkbox - dropbox.js Sample Application
+
+
+
+
+
+
+
+
+
+
+
+
+ checkbox
+ powered by
+ dropbox
+
+
+
+
+
+ Sign out
+
+
+
+
Active
+
+
+
+
Done
+
+
+
+
+
+
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/lib/coffee-script.js b/lib/client/storage/dropbox/samples/checkbox.js/public/lib/coffee-script.js
new file mode 100644
index 00000000..66c387f8
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/public/lib/coffee-script.js
@@ -0,0 +1,8 @@
+/**
+ * CoffeeScript Compiler v1.4.0
+ * http://coffeescript.org
+ *
+ * Copyright 2011, Jeremy Ashkenas
+ * Released under the MIT License
+ */
+(function(root){var CoffeeScript=function(){function require(a){return require[a]}return require["./helpers"]=new function(){var a=this;((function(){var b,c,d;a.starts=function(a,b,c){return b===a.substr(c,b.length)},a.ends=function(a,b,c){var d;return d=b.length,b===a.substr(a.length-d-(c||0),d)},a.compact=function(a){var b,c,d,e;e=[];for(c=0,d=a.length;c=0)f+=1;else if(j=g[0],t.call(d,j)>=0)f-=1;a+=1}return a-1},a.prototype.removeLeadingNewlines=function(){var a,b,c,d,e;e=this.tokens;for(a=c=0,d=e.length;c=0)?(d.splice(b,1),0):1})},a.prototype.closeOpenCalls=function(){var a,b;return b=function(a,b){var c;return(c=a[0])===")"||c==="CALL_END"||a[0]==="OUTDENT"&&this.tag(b-1)===")"},a=function(a,b){return this.tokens[a[0]==="OUTDENT"?b-1:b][0]="CALL_END"},this.scanTokens(function(c,d){return c[0]==="CALL_START"&&this.detectEnd(d+1,b,a),1})},a.prototype.closeOpenIndexes=function(){var a,b;return b=function(a,b){var c;return(c=a[0])==="]"||c==="INDEX_END"},a=function(a,b){return a[0]="INDEX_END"},this.scanTokens(function(c,d){return c[0]==="INDEX_START"&&this.detectEnd(d+1,b,a),1})},a.prototype.addImplicitBraces=function(){var a,b,c,f,g,i,j,k;return f=[],g=null,k=null,c=!0,i=0,j=0,b=function(a,b){var d,e,f,g,i,m;return i=this.tokens.slice(b+1,+(b+3)+1||9e9),d=i[0],g=i[1],f=i[2],"HERECOMMENT"===(d!=null?d[0]:void 0)?!1:(e=a[0],t.call(l,e)>=0&&(c=!1),(e==="TERMINATOR"||e==="OUTDENT"||t.call(h,e)>=0&&c&&b-j!==1)&&(!k&&this.tag(b-1)!==","||(g!=null?g[0]:void 0)!==":"&&((d!=null?d[0]:void 0)!=="@"||(f!=null?f[0]:void 0)!==":"))||e===","&&d&&(m=d[0])!=="IDENTIFIER"&&m!=="NUMBER"&&m!=="STRING"&&m!=="@"&&m!=="TERMINATOR"&&m!=="OUTDENT")},a=function(a,b){var c;return c=this.generate("}","}",a[2]),this.tokens.splice(b,0,c)},this.scanTokens(function(h,i,m){var n,o,p,q,r,s,u,v;if(u=q=h[0],t.call(e,u)>=0)return f.push([q==="INDENT"&&this.tag(i-1)==="{"?"{":q,i]),1;if(t.call(d,q)>=0)return g=f.pop(),1;if(q!==":"||(n=this.tag(i-2))!==":"&&((v=f[f.length-1])!=null?v[0]:void 0)==="{")return 1;c=!0,j=i+1,f.push(["{"]),o=n==="@"?i-2:i-1;while(this.tag(o-2)==="HERECOMMENT")o-=2;return p=this.tag(o-1),k=!p||t.call(l,p)>=0,s=new String("{"),s.generated=!0,r=this.generate("{",s,h[2]),m.splice(o,0,r),this.detectEnd(i+2,b,a),2})},a.prototype.addImplicitParentheses=function(){var a,b,c,d,e;return c=e=d=!1,b=function(a,b){var c,g,i,j;g=a[0];if(!e&&a.fromThen)return!0;if(g==="IF"||g==="ELSE"||g==="CATCH"||g==="->"||g==="=>"||g==="CLASS")e=!0;if(g==="IF"||g==="ELSE"||g==="SWITCH"||g==="TRY"||g==="=")d=!0;return g!=="."&&g!=="?."&&g!=="::"||this.tag(b-1)!=="OUTDENT"?!a.generated&&this.tag(b-1)!==","&&(t.call(h,g)>=0||g==="INDENT"&&!d)&&(g!=="INDENT"||(i=this.tag(b-2))!=="CLASS"&&i!=="EXTENDS"&&(j=this.tag(b-1),t.call(f,j)<0)&&(!(c=this.tokens[b+1])||!c.generated||c[0]!=="{")):!0},a=function(a,b){return this.tokens.splice(b,0,this.generate("CALL_END",")",a[2]))},this.scanTokens(function(f,h,k){var m,n,o,p,q,r,s,u;q=f[0];if(q==="CLASS"||q==="IF"||q==="FOR"||q==="WHILE")c=!0;return r=k.slice(h-1,+(h+1)+1||9e9),p=r[0],n=r[1],o=r[2],m=!c&&q==="INDENT"&&o&&o.generated&&o[0]==="{"&&p&&(s=p[0],t.call(i,s)>=0),e=!1,d=!1,t.call(l,q)>=0&&(c=!1),p&&!p.spaced&&q==="?"&&(f.call=!0),f.fromThen?1:m||(p!=null?p.spaced:void 0)&&(p.call||(u=p[0],t.call(i,u)>=0))&&(t.call(g,q)>=0||!f.spaced&&!f.newLine&&t.call(j,q)>=0)?(k.splice(h,0,this.generate("CALL_START","(",f[2])),this.detectEnd(h+1,b,a),p[0]==="?"&&(p[0]="FUNC_EXIST"),2):1})},a.prototype.addImplicitIndentation=function(){var a,b,c,d,e;return e=c=d=null,b=function(a,b){var c;return a[1]!==";"&&(c=a[0],t.call(m,c)>=0)&&(a[0]!=="ELSE"||e==="IF"||e==="THEN")},a=function(a,b){return this.tokens.splice(this.tag(b-1)===","?b-1:b,0,d)},this.scanTokens(function(f,g,h){var i,j,k;return i=f[0],i==="TERMINATOR"&&this.tag(g+1)==="THEN"?(h.splice(g,1),0):i==="ELSE"&&this.tag(g-1)!=="OUTDENT"?(h.splice.apply(h,[g,0].concat(u.call(this.indentation(f)))),2):i!=="CATCH"||(j=this.tag(g+2))!=="OUTDENT"&&j!=="TERMINATOR"&&j!=="FINALLY"?t.call(n,i)>=0&&this.tag(g+1)!=="INDENT"&&(i!=="ELSE"||this.tag(g+1)!=="IF")?(e=i,k=this.indentation(f,!0),c=k[0],d=k[1],e==="THEN"&&(c.fromThen=!0),h.splice(g+1,0,c),this.detectEnd(g+2,b,a),i==="THEN"&&h.splice(g,1),1):1:(h.splice.apply(h,[g+2,0].concat(u.call(this.indentation(f)))),4)})},a.prototype.tagPostfixConditionals=function(){var a,b,c;return c=null,b=function(a,b){var c;return(c=a[0])==="TERMINATOR"||c==="INDENT"},a=function(a,b){if(a[0]!=="INDENT"||a.generated&&!a.fromThen)return c[0]="POST_"+c[0]},this.scanTokens(function(d,e){return d[0]!=="IF"?1:(c=d,this.detectEnd(e+1,b,a),1)})},a.prototype.indentation=function(a,b){var c,d;return b==null&&(b=!1),c=["INDENT",2,a[2]],d=["OUTDENT",2,a[2]],b&&(c.generated=d.generated=!0),[c,d]},a.prototype.generate=function(a,b,c){var d;return d=[a,b,c],d.generated=!0,d},a.prototype.tag=function(a){var b;return(b=this.tokens[a])!=null?b[0]:void 0},a}(),b=[["(",")"],["[","]"],["{","}"],["INDENT","OUTDENT"],["CALL_START","CALL_END"],["PARAM_START","PARAM_END"],["INDEX_START","INDEX_END"]],a.INVERSES=k={},e=[],d=[];for(q=0,r=b.length;q","=>","[","(","{","--","++"],j=["+","-"],f=["->","=>","{","[",","],h=["POST_IF","FOR","WHILE","UNTIL","WHEN","BY","LOOP","TERMINATOR"],n=["ELSE","->","=>","TRY","FINALLY","THEN"],m=["TERMINATOR","CATCH","FINALLY","ELSE","OUTDENT","LEADING_WHEN"],l=["TERMINATOR","INDENT","OUTDENT"]})).call(this)},require["./lexer"]=new function(){var a=this;((function(){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X=[].indexOf||function(a){for(var b=0,c=this.length;b=0||X.call(g,c)>=0)&&(j=c.toUpperCase(),j==="WHEN"&&(l=this.tag(),X.call(v,l)>=0)?j="LEADING_WHEN":j==="FOR"?this.seenFor=!0:j==="UNLESS"?j="IF":X.call(O,j)>=0?j="UNARY":X.call(H,j)>=0&&(j!=="INSTANCEOF"&&this.seenFor?(j="FOR"+j,this.seenFor=!1):(j="RELATION",this.value()==="!"&&(this.tokens.pop(),c="!"+c)))),X.call(t,c)>=0&&(b?(j="IDENTIFIER",c=new String(c),c.reserved=!0):X.call(I,c)>=0&&this.error('reserved word "'+c+'"')),b||(X.call(e,c)>=0&&(c=f[c]),j=function(){switch(c){case"!":return"UNARY";case"==":case"!=":return"COMPARE";case"&&":case"||":return"LOGIC";case"true":case"false":return"BOOL";case"break":case"continue":return"STATEMENT";default:return j}}()),this.token(j,c),a&&this.token(":",":"),d.length)):0},a.prototype.numberToken=function(){var a,b,c,d,e;if(!(c=E.exec(this.chunk)))return 0;d=c[0],/^0[BOX]/.test(d)?this.error("radix prefix '"+d+"' must be lowercase"):/E/.test(d)&&!/^0x/.test(d)?this.error("exponential notation '"+d+"' must be indicated with a lowercase 'e'"):/^0\d*[89]/.test(d)?this.error("decimal literal '"+d+"' must not be prefixed with '0'"):/^0\d+/.test(d)&&this.error("octal literal '"+d+"' must be prefixed with '0o'"),b=d.length;if(e=/^0o([0-7]+)/.exec(d))d="0x"+parseInt(e[1],8).toString(16);if(a=/^0b([01]+)/.exec(d))d="0x"+parseInt(a[1],2).toString(16);return this.token("NUMBER",d),b},a.prototype.stringToken=function(){var a,b,c;switch(this.chunk.charAt(0)){case"'":if(!(a=L.exec(this.chunk)))return 0;this.token("STRING",(c=a[0]).replace(A,"\\\n"));break;case'"':if(!(c=this.balancedString(this.chunk,'"')))return 0;0=0)?0:(c=G.exec(this.chunk))?(g=c,c=g[0],e=g[1],a=g[2],e.slice(0,2)==="/*"&&this.error("regular expressions cannot begin with `*`"),e==="//"&&(e="/(?:)/"),this.token("REGEX",""+e+a),c.length):0)},a.prototype.heregexToken=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n;d=a[0],b=a[1],c=a[2];if(0>b.indexOf("#{"))return e=b.replace(o,"").replace(/\//g,"\\/"),e.match(/^\*/)&&this.error("regular expressions cannot begin with `*`"),this.token("REGEX","/"+(e||"(?:)")+"/"+c),d.length;this.token("IDENTIFIER","RegExp"),this.tokens.push(["CALL_START","("]),g=[],k=this.interpolateString(b,{regex:!0});for(i=0,j=k.length;ithis.indent){if(d)return this.indebt=e-this.indent,this.suppressNewlines(),b.length;a=e-this.indent+this.outdebt,this.token("INDENT",a),this.indents.push(a),this.ends.push("OUTDENT"),this.outdebt=this.indebt=0}else this.indebt=0,this.outdentToken(this.indent-e,d);return this.indent=e,b.length},a.prototype.outdentToken=function(a,b){var c,d;while(a>0)d=this.indents.length-1,this.indents[d]===void 0?a=0:this.indents[d]===this.outdebt?(a-=this.outdebt,this.outdebt=0):this.indents[d]=0)&&this.error('reserved word "'+this.value()+"\" can't be assigned");if((h=b[1])==="||"||h==="&&")return b[0]="COMPOUND_ASSIGN",b[1]+="=",f.length}if(f===";")this.seenFor=!1,e="TERMINATOR";else if(X.call(z,f)>=0)e="MATH";else if(X.call(i,f)>=0)e="COMPARE";else if(X.call(j,f)>=0)e="COMPOUND_ASSIGN";else if(X.call(O,f)>=0)e="UNARY";else if(X.call(K,f)>=0)e="SHIFT";else if(X.call(x,f)>=0||f==="?"&&(b!=null?b.spaced:void 0))e="LOGIC";else if(b&&!b.spaced)if(f==="("&&(k=b[0],X.call(c,k)>=0))b[0]==="?"&&(b[0]="FUNC_EXIST"),e="CALL_START";else if(f==="["&&(l=b[0],X.call(q,l)>=0)){e="INDEX_START";switch(b[0]){case"?":b[0]="INDEX_SOAK"}}switch(f){case"(":case"{":case"[":this.ends.push(r[f]);break;case")":case"}":case"]":this.pair(f)}return this.token(e,f),f.length},a.prototype.sanitizeHeredoc=function(a,b){var c,d,e,f,g;e=b.indent,d=b.herecomment;if(d){l.test(a)&&this.error('block comment cannot contain "*/", starting');if(a.indexOf("\n")<=0)return a}else while(f=m.exec(a)){c=f[1];if(e===null||0<(g=c.length)&&gj;d=1<=j?++i:--i){if(c){--c;continue}switch(e=a.charAt(d)){case"\\":++c;continue;case b:h.pop();if(!h.length)return a.slice(0,+d+1||9e9);b=h[h.length-1];continue}b!=="}"||e!=='"'&&e!=="'"?b==="}"&&e==="/"&&(f=n.exec(a.slice(d))||G.exec(a.slice(d)))?c+=f[0].length-1:b==="}"&&e==="{"?h.push(b="}"):b==='"'&&g==="#"&&e==="{"&&h.push(b="}"):h.push(b=e),g=e}return this.error("missing "+h.pop()+", starting")},a.prototype.interpolateString=function(b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;c==null&&(c={}),e=c.heredoc,m=c.regex,o=[],l=0,f=-1;while(j=b.charAt(f+=1)){if(j==="\\"){f+=1;continue}if(j!=="#"||b.charAt(f+1)!=="{"||!(d=this.balancedString(b.slice(f+1),"}")))continue;l1&&(k.unshift(["(","(",this.line]),k.push([")",")",this.line])),o.push(["TOKENS",k])}f+=d.length,l=f+1}f>l&&l1)&&this.token("(","(");for(f=q=0,r=o.length;q|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/,P=/^[^\n\S]+/,h=/^###([^#][\s\S]*?)(?:###[^\n\S]*|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/,d=/^[-=]>/,B=/^(?:\n[^\n\S]*)+/,L=/^'[^\\']*(?:\\.[^\\']*)*'/,s=/^`[^\\`]*(?:\\.[^\\`]*)*`/,G=/^(\/(?![\s=])[^[\/\n\\]*(?:(?:\\[\s\S]|\[[^\]\n\\]*(?:\\[\s\S][^\]\n\\]*)*])[^[\/\n\\]*)*\/)([imgy]{0,4})(?!\w)/,n=/^\/{3}([\s\S]+?)\/{3}([imgy]{0,4})(?!\w)/,o=/\s+(?:#.*)?/g,A=/\n/g,m=/\n+([^\n\S]*)/g,l=/\*\//,w=/^\s*(?:,|\??\.(?![.\d])|::)/,N=/\s+$/,j=["-=","+=","/=","*=","%=","||=","&&=","?=","<<=",">>=",">>>=","&=","^=","|="],O=["!","~","NEW","TYPEOF","DELETE","DO"],x=["&&","||","&","|","^"],K=["<<",">>",">>>"],i=["==","!=","<",">","<=",">="],z=["*","/","%"],H=["IN","OF","INSTANCEOF"],b=["TRUE","FALSE"],C=["NUMBER","REGEX","BOOL","NULL","UNDEFINED","++","--","]"],D=C.concat(")","}","THIS","IDENTIFIER","STRING"),c=["IDENTIFIER","STRING","REGEX",")","]","}","?","::","@","THIS","SUPER"],q=c.concat("NUMBER","BOOL","NULL","UNDEFINED"),v=["INDENT","OUTDENT","TERMINATOR"]})).call(this)},require["./parser"]=new function(){var a=this,b=function(){var a={trace:function(){},yy:{},symbols_:{error:2,Root:3,Body:4,Block:5,TERMINATOR:6,Line:7,Expression:8,Statement:9,Return:10,Comment:11,STATEMENT:12,Value:13,Invocation:14,Code:15,Operation:16,Assign:17,If:18,Try:19,While:20,For:21,Switch:22,Class:23,Throw:24,INDENT:25,OUTDENT:26,Identifier:27,IDENTIFIER:28,AlphaNumeric:29,NUMBER:30,STRING:31,Literal:32,JS:33,REGEX:34,DEBUGGER:35,UNDEFINED:36,NULL:37,BOOL:38,Assignable:39,"=":40,AssignObj:41,ObjAssignable:42,":":43,ThisProperty:44,RETURN:45,HERECOMMENT:46,PARAM_START:47,ParamList:48,PARAM_END:49,FuncGlyph:50,"->":51,"=>":52,OptComma:53,",":54,Param:55,ParamVar:56,"...":57,Array:58,Object:59,Splat:60,SimpleAssignable:61,Accessor:62,Parenthetical:63,Range:64,This:65,".":66,"?.":67,"::":68,Index:69,INDEX_START:70,IndexValue:71,INDEX_END:72,INDEX_SOAK:73,Slice:74,"{":75,AssignList:76,"}":77,CLASS:78,EXTENDS:79,OptFuncExist:80,Arguments:81,SUPER:82,FUNC_EXIST:83,CALL_START:84,CALL_END:85,ArgList:86,THIS:87,"@":88,"[":89,"]":90,RangeDots:91,"..":92,Arg:93,SimpleArgs:94,TRY:95,Catch:96,FINALLY:97,CATCH:98,THROW:99,"(":100,")":101,WhileSource:102,WHILE:103,WHEN:104,UNTIL:105,Loop:106,LOOP:107,ForBody:108,FOR:109,ForStart:110,ForSource:111,ForVariables:112,OWN:113,ForValue:114,FORIN:115,FOROF:116,BY:117,SWITCH:118,Whens:119,ELSE:120,When:121,LEADING_WHEN:122,IfBlock:123,IF:124,POST_IF:125,UNARY:126,"-":127,"+":128,"--":129,"++":130,"?":131,MATH:132,SHIFT:133,COMPARE:134,LOGIC:135,RELATION:136,COMPOUND_ASSIGN:137,$accept:0,$end:1},terminals_:{2:"error",6:"TERMINATOR",12:"STATEMENT",25:"INDENT",26:"OUTDENT",28:"IDENTIFIER",30:"NUMBER",31:"STRING",33:"JS",34:"REGEX",35:"DEBUGGER",36:"UNDEFINED",37:"NULL",38:"BOOL",40:"=",43:":",45:"RETURN",46:"HERECOMMENT",47:"PARAM_START",49:"PARAM_END",51:"->",52:"=>",54:",",57:"...",66:".",67:"?.",68:"::",70:"INDEX_START",72:"INDEX_END",73:"INDEX_SOAK",75:"{",77:"}",78:"CLASS",79:"EXTENDS",82:"SUPER",83:"FUNC_EXIST",84:"CALL_START",85:"CALL_END",87:"THIS",88:"@",89:"[",90:"]",92:"..",95:"TRY",97:"FINALLY",98:"CATCH",99:"THROW",100:"(",101:")",103:"WHILE",104:"WHEN",105:"UNTIL",107:"LOOP",109:"FOR",113:"OWN",115:"FORIN",116:"FOROF",117:"BY",118:"SWITCH",120:"ELSE",122:"LEADING_WHEN",124:"IF",125:"POST_IF",126:"UNARY",127:"-",128:"+",129:"--",130:"++",131:"?",132:"MATH",133:"SHIFT",134:"COMPARE",135:"LOGIC",136:"RELATION",137:"COMPOUND_ASSIGN"},productions_:[0,[3,0],[3,1],[3,2],[4,1],[4,3],[4,2],[7,1],[7,1],[9,1],[9,1],[9,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[8,1],[5,2],[5,3],[27,1],[29,1],[29,1],[32,1],[32,1],[32,1],[32,1],[32,1],[32,1],[32,1],[17,3],[17,4],[17,5],[41,1],[41,3],[41,5],[41,1],[42,1],[42,1],[42,1],[10,2],[10,1],[11,1],[15,5],[15,2],[50,1],[50,1],[53,0],[53,1],[48,0],[48,1],[48,3],[48,4],[48,6],[55,1],[55,2],[55,3],[56,1],[56,1],[56,1],[56,1],[60,2],[61,1],[61,2],[61,2],[61,1],[39,1],[39,1],[39,1],[13,1],[13,1],[13,1],[13,1],[13,1],[62,2],[62,2],[62,2],[62,1],[62,1],[69,3],[69,2],[71,1],[71,1],[59,4],[76,0],[76,1],[76,3],[76,4],[76,6],[23,1],[23,2],[23,3],[23,4],[23,2],[23,3],[23,4],[23,5],[14,3],[14,3],[14,1],[14,2],[80,0],[80,1],[81,2],[81,4],[65,1],[65,1],[44,2],[58,2],[58,4],[91,1],[91,1],[64,5],[74,3],[74,2],[74,2],[74,1],[86,1],[86,3],[86,4],[86,4],[86,6],[93,1],[93,1],[94,1],[94,3],[19,2],[19,3],[19,4],[19,5],[96,3],[24,2],[63,3],[63,5],[102,2],[102,4],[102,2],[102,4],[20,2],[20,2],[20,2],[20,1],[106,2],[106,2],[21,2],[21,2],[21,2],[108,2],[108,2],[110,2],[110,3],[114,1],[114,1],[114,1],[114,1],[112,1],[112,3],[111,2],[111,2],[111,4],[111,4],[111,4],[111,6],[111,6],[22,5],[22,7],[22,4],[22,6],[119,1],[119,2],[121,3],[121,4],[123,3],[123,5],[18,1],[18,3],[18,3],[18,3],[16,2],[16,2],[16,2],[16,2],[16,2],[16,2],[16,2],[16,2],[16,3],[16,3],[16,3],[16,3],[16,3],[16,3],[16,3],[16,3],[16,5],[16,3]],performAction:function(b,c,d,e,f,g,h){var i=g.length-1;switch(f){case 1:return this.$=new e.Block;case 2:return this.$=g[i];case 3:return this.$=g[i-1];case 4:this.$=e.Block.wrap([g[i]]);break;case 5:this.$=g[i-2].push(g[i]);break;case 6:this.$=g[i-1];break;case 7:this.$=g[i];break;case 8:this.$=g[i];break;case 9:this.$=g[i];break;case 10:this.$=g[i];break;case 11:this.$=new e.Literal(g[i]);break;case 12:this.$=g[i];break;case 13:this.$=g[i];break;case 14:this.$=g[i];break;case 15:this.$=g[i];break;case 16:this.$=g[i];break;case 17:this.$=g[i];break;case 18:this.$=g[i];break;case 19:this.$=g[i];break;case 20:this.$=g[i];break;case 21:this.$=g[i];break;case 22:this.$=g[i];break;case 23:this.$=g[i];break;case 24:this.$=new e.Block;break;case 25:this.$=g[i-1];break;case 26:this.$=new e.Literal(g[i]);break;case 27:this.$=new e.Literal(g[i]);break;case 28:this.$=new e.Literal(g[i]);break;case 29:this.$=g[i];break;case 30:this.$=new e.Literal(g[i]);break;case 31:this.$=new e.Literal(g[i]);break;case 32:this.$=new e.Literal(g[i]);break;case 33:this.$=new e.Undefined;break;case 34:this.$=new e.Null;break;case 35:this.$=new e.Bool(g[i]);break;case 36:this.$=new e.Assign(g[i-2],g[i]);break;case 37:this.$=new e.Assign(g[i-3],g[i]);break;case 38:this.$=new e.Assign(g[i-4],g[i-1]);break;case 39:this.$=new e.Value(g[i]);break;case 40:this.$=new e.Assign(new e.Value(g[i-2]),g[i],"object");break;case 41:this.$=new e.Assign(new e.Value(g[i-4]),g[i-1],"object");break;case 42:this.$=g[i];break;case 43:this.$=g[i];break;case 44:this.$=g[i];break;case 45:this.$=g[i];break;case 46:this.$=new e.Return(g[i]);break;case 47:this.$=new e.Return;break;case 48:this.$=new e.Comment(g[i]);break;case 49:this.$=new e.Code(g[i-3],g[i],g[i-1]);break;case 50:this.$=new e.Code([],g[i],g[i-1]);break;case 51:this.$="func";break;case 52:this.$="boundfunc";break;case 53:this.$=g[i];break;case 54:this.$=g[i];break;case 55:this.$=[];break;case 56:this.$=[g[i]];break;case 57:this.$=g[i-2].concat(g[i]);break;case 58:this.$=g[i-3].concat(g[i]);break;case 59:this.$=g[i-5].concat(g[i-2]);break;case 60:this.$=new e.Param(g[i]);break;case 61:this.$=new e.Param(g[i-1],null,!0);break;case 62:this.$=new e.Param(g[i-2],g[i]);break;case 63:this.$=g[i];break;case 64:this.$=g[i];break;case 65:this.$=g[i];break;case 66:this.$=g[i];break;case 67:this.$=new e.Splat(g[i-1]);break;case 68:this.$=new e.Value(g[i]);break;case 69:this.$=g[i-1].add(g[i]);break;case 70:this.$=new e.Value(g[i-1],[].concat(g[i]));break;case 71:this.$=g[i];break;case 72:this.$=g[i];break;case 73:this.$=new e.Value(g[i]);break;case 74:this.$=new e.Value(g[i]);break;case 75:this.$=g[i];break;case 76:this.$=new e.Value(g[i]);break;case 77:this.$=new e.Value(g[i]);break;case 78:this.$=new e.Value(g[i]);break;case 79:this.$=g[i];break;case 80:this.$=new e.Access(g[i]);break;case 81:this.$=new e.Access(g[i],"soak");break;case 82:this.$=[new e.Access(new e.Literal("prototype")),new e.Access(g[i])];break;case 83:this.$=new e.Access(new e.Literal("prototype"));break;case 84:this.$=g[i];break;case 85:this.$=g[i-1];break;case 86:this.$=e.extend(g[i],{soak:!0});break;case 87:this.$=new e.Index(g[i]);break;case 88:this.$=new e.Slice(g[i]);break;case 89:this.$=new e.Obj(g[i-2],g[i-3].generated);break;case 90:this.$=[];break;case 91:this.$=[g[i]];break;case 92:this.$=g[i-2].concat(g[i]);break;case 93:this.$=g[i-3].concat(g[i]);break;case 94:this.$=g[i-5].concat(g[i-2]);break;case 95:this.$=new e.Class;break;case 96:this.$=new e.Class(null,null,g[i]);break;case 97:this.$=new e.Class(null,g[i]);break;case 98:this.$=new e.Class(null,g[i-1],g[i]);break;case 99:this.$=new e.Class(g[i]);break;case 100:this.$=new e.Class(g[i-1],null,g[i]);break;case 101:this.$=new e.Class(g[i-2],g[i]);break;case 102:this.$=new e.Class(g[i-3],g[i-1],g[i]);break;case 103:this.$=new e.Call(g[i-2],g[i],g[i-1]);break;case 104:this.$=new e.Call(g[i-2],g[i],g[i-1]);break;case 105:this.$=new e.Call("super",[new e.Splat(new e.Literal("arguments"))]);break;case 106:this.$=new e.Call("super",g[i]);break;case 107:this.$=!1;break;case 108:this.$=!0;break;case 109:this.$=[];break;case 110:this.$=g[i-2];break;case 111:this.$=new e.Value(new e.Literal("this"));break;case 112:this.$=new e.Value(new e.Literal("this"));break;case 113:this.$=new e.Value(new e.Literal("this"),[new e.Access(g[i])],"this");break;case 114:this.$=new e.Arr([]);break;case 115:this.$=new e.Arr(g[i-2]);break;case 116:this.$="inclusive";break;case 117:this.$="exclusive";break;case 118:this.$=new e.Range(g[i-3],g[i-1],g[i-2]);break;case 119:this.$=new e.Range(g[i-2],g[i],g[i-1]);break;case 120:this.$=new e.Range(g[i-1],null,g[i]);break;case 121:this.$=new e.Range(null,g[i],g[i-1]);break;case 122:this.$=new e.Range(null,null,g[i]);break;case 123:this.$=[g[i]];break;case 124:this.$=g[i-2].concat(g[i]);break;case 125:this.$=g[i-3].concat(g[i]);break;case 126:this.$=g[i-2];break;case 127:this.$=g[i-5].concat(g[i-2]);break;case 128:this.$=g[i];break;case 129:this.$=g[i];break;case 130:this.$=g[i];break;case 131:this.$=[].concat(g[i-2],g[i]);break;case 132:this.$=new e.Try(g[i]);break;case 133:this.$=new e.Try(g[i-1],g[i][0],g[i][1]);break;case 134:this.$=new e.Try(g[i-2],null,null,g[i]);break;case 135:this.$=new e.Try(g[i-3],g[i-2][0],g[i-2][1],g[i]);break;case 136:this.$=[g[i-1],g[i]];break;case 137:this.$=new e.Throw(g[i]);break;case 138:this.$=new e.Parens(g[i-1]);break;case 139:this.$=new e.Parens(g[i-2]);break;case 140:this.$=new e.While(g[i]);break;case 141:this.$=new e.While(g[i-2],{guard:g[i]});break;case 142:this.$=new e.While(g[i],{invert:!0});break;case 143:this.$=new e.While(g[i-2],{invert:!0,guard:g[i]});break;case 144:this.$=g[i-1].addBody(g[i]);break;case 145:this.$=g[i].addBody(e.Block.wrap([g[i-1]]));break;case 146:this.$=g[i].addBody(e.Block.wrap([g[i-1]]));break;case 147:this.$=g[i];break;case 148:this.$=(new e.While(new e.Literal("true"))).addBody(g[i]);break;case 149:this.$=(new e.While(new e.Literal("true"))).addBody(e.Block.wrap([g[i]]));break;case 150:this.$=new e.For(g[i-1],g[i]);break;case 151:this.$=new e.For(g[i-1],g[i]);break;case 152:this.$=new e.For(g[i],g[i-1]);break;case 153:this.$={source:new e.Value(g[i])};break;case 154:this.$=function(){return g[i].own=g[i-1].own,g[i].name=g[i-1][0],g[i].index=g[i-1][1],g[i]}();break;case 155:this.$=g[i];break;case 156:this.$=function(){return g[i].own=!0,g[i]}();break;case 157:this.$=g[i];break;case 158:this.$=g[i];break;case 159:this.$=new e.Value(g[i]);break;case 160:this.$=new e.Value(g[i]);break;case 161:this.$=[g[i]];break;case 162:this.$=[g[i-2],g[i]];break;case 163:this.$={source:g[i]};break;case 164:this.$={source:g[i],object:!0};break;case 165:this.$={source:g[i-2],guard:g[i]};break;case 166:this.$={source:g[i-2],guard:g[i],object:!0};break;case 167:this.$={source:g[i-2],step:g[i]};break;case 168:this.$={source:g[i-4],guard:g[i-2],step:g[i]};break;case 169:this.$={source:g[i-4],step:g[i-2],guard:g[i]};break;case 170:this.$=new e.Switch(g[i-3],g[i-1]);break;case 171:this.$=new e.Switch(g[i-5],g[i-3],g[i-1]);break;case 172:this.$=new e.Switch(null,g[i-1]);break;case 173:this.$=new e.Switch(null,g[i-3],g[i-1]);break;case 174:this.$=g[i];break;case 175:this.$=g[i-1].concat(g[i]);break;case 176:this.$=[[g[i-1],g[i]]];break;case 177:this.$=[[g[i-2],g[i-1]]];break;case 178:this.$=new e.If(g[i-1],g[i],{type:g[i-2]});break;case 179:this.$=g[i-4].addElse(new e.If(g[i-1],g[i],{type:g[i-2]}));break;case 180:this.$=g[i];break;case 181:this.$=g[i-2].addElse(g[i]);break;case 182:this.$=new e.If(g[i],e.Block.wrap([g[i-2]]),{type:g[i-1],statement:!0});break;case 183:this.$=new e.If(g[i],e.Block.wrap([g[i-2]]),{type:g[i-1],statement:!0});break;case 184:this.$=new e.Op(g[i-1],g[i]);break;case 185:this.$=new e.Op("-",g[i]);break;case 186:this.$=new e.Op("+",g[i]);break;case 187:this.$=new e.Op("--",g[i]);break;case 188:this.$=new e.Op("++",g[i]);break;case 189:this.$=new e.Op("--",g[i-1],null,!0);break;case 190:this.$=new e.Op("++",g[i-1],null,!0);break;case 191:this.$=new e.Existence(g[i-1]);break;case 192:this.$=new e.Op("+",g[i-2],g[i]);break;case 193:this.$=new e.Op("-",g[i-2],g[i]);break;case 194:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 195:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 196:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 197:this.$=new e.Op(g[i-1],g[i-2],g[i]);break;case 198:this.$=function(){return g[i-1].charAt(0)==="!"?(new e.Op(g[i-1].slice(1),g[i-2],g[i])).invert():new e.Op(g[i-1],g[i-2],g[i])}();break;case 199:this.$=new e.Assign(g[i-2],g[i],g[i-1]);break;case 200:this.$=new e.Assign(g[i-4],g[i-1],g[i-3]);break;case 201:this.$=new e.Extends(g[i-2],g[i])}},table:[{1:[2,1],3:1,4:2,5:3,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,5],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[3]},{1:[2,2],6:[1,74]},{6:[1,75]},{1:[2,4],6:[2,4],26:[2,4],101:[2,4]},{4:77,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,26:[1,76],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,7],6:[2,7],26:[2,7],101:[2,7],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,8],6:[2,8],26:[2,8],101:[2,8],102:90,103:[1,65],105:[1,66],108:91,109:[1,68],110:69,125:[1,89]},{1:[2,12],6:[2,12],25:[2,12],26:[2,12],49:[2,12],54:[2,12],57:[2,12],62:93,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],72:[2,12],73:[1,100],77:[2,12],80:92,83:[1,94],84:[2,107],85:[2,12],90:[2,12],92:[2,12],101:[2,12],103:[2,12],104:[2,12],105:[2,12],109:[2,12],117:[2,12],125:[2,12],127:[2,12],128:[2,12],131:[2,12],132:[2,12],133:[2,12],134:[2,12],135:[2,12],136:[2,12]},{1:[2,13],6:[2,13],25:[2,13],26:[2,13],49:[2,13],54:[2,13],57:[2,13],62:102,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],72:[2,13],73:[1,100],77:[2,13],80:101,83:[1,94],84:[2,107],85:[2,13],90:[2,13],92:[2,13],101:[2,13],103:[2,13],104:[2,13],105:[2,13],109:[2,13],117:[2,13],125:[2,13],127:[2,13],128:[2,13],131:[2,13],132:[2,13],133:[2,13],134:[2,13],135:[2,13],136:[2,13]},{1:[2,14],6:[2,14],25:[2,14],26:[2,14],49:[2,14],54:[2,14],57:[2,14],72:[2,14],77:[2,14],85:[2,14],90:[2,14],92:[2,14],101:[2,14],103:[2,14],104:[2,14],105:[2,14],109:[2,14],117:[2,14],125:[2,14],127:[2,14],128:[2,14],131:[2,14],132:[2,14],133:[2,14],134:[2,14],135:[2,14],136:[2,14]},{1:[2,15],6:[2,15],25:[2,15],26:[2,15],49:[2,15],54:[2,15],57:[2,15],72:[2,15],77:[2,15],85:[2,15],90:[2,15],92:[2,15],101:[2,15],103:[2,15],104:[2,15],105:[2,15],109:[2,15],117:[2,15],125:[2,15],127:[2,15],128:[2,15],131:[2,15],132:[2,15],133:[2,15],134:[2,15],135:[2,15],136:[2,15]},{1:[2,16],6:[2,16],25:[2,16],26:[2,16],49:[2,16],54:[2,16],57:[2,16],72:[2,16],77:[2,16],85:[2,16],90:[2,16],92:[2,16],101:[2,16],103:[2,16],104:[2,16],105:[2,16],109:[2,16],117:[2,16],125:[2,16],127:[2,16],128:[2,16],131:[2,16],132:[2,16],133:[2,16],134:[2,16],135:[2,16],136:[2,16]},{1:[2,17],6:[2,17],25:[2,17],26:[2,17],49:[2,17],54:[2,17],57:[2,17],72:[2,17],77:[2,17],85:[2,17],90:[2,17],92:[2,17],101:[2,17],103:[2,17],104:[2,17],105:[2,17],109:[2,17],117:[2,17],125:[2,17],127:[2,17],128:[2,17],131:[2,17],132:[2,17],133:[2,17],134:[2,17],135:[2,17],136:[2,17]},{1:[2,18],6:[2,18],25:[2,18],26:[2,18],49:[2,18],54:[2,18],57:[2,18],72:[2,18],77:[2,18],85:[2,18],90:[2,18],92:[2,18],101:[2,18],103:[2,18],104:[2,18],105:[2,18],109:[2,18],117:[2,18],125:[2,18],127:[2,18],128:[2,18],131:[2,18],132:[2,18],133:[2,18],134:[2,18],135:[2,18],136:[2,18]},{1:[2,19],6:[2,19],25:[2,19],26:[2,19],49:[2,19],54:[2,19],57:[2,19],72:[2,19],77:[2,19],85:[2,19],90:[2,19],92:[2,19],101:[2,19],103:[2,19],104:[2,19],105:[2,19],109:[2,19],117:[2,19],125:[2,19],127:[2,19],128:[2,19],131:[2,19],132:[2,19],133:[2,19],134:[2,19],135:[2,19],136:[2,19]},{1:[2,20],6:[2,20],25:[2,20],26:[2,20],49:[2,20],54:[2,20],57:[2,20],72:[2,20],77:[2,20],85:[2,20],90:[2,20],92:[2,20],101:[2,20],103:[2,20],104:[2,20],105:[2,20],109:[2,20],117:[2,20],125:[2,20],127:[2,20],128:[2,20],131:[2,20],132:[2,20],133:[2,20],134:[2,20],135:[2,20],136:[2,20]},{1:[2,21],6:[2,21],25:[2,21],26:[2,21],49:[2,21],54:[2,21],57:[2,21],72:[2,21],77:[2,21],85:[2,21],90:[2,21],92:[2,21],101:[2,21],103:[2,21],104:[2,21],105:[2,21],109:[2,21],117:[2,21],125:[2,21],127:[2,21],128:[2,21],131:[2,21],132:[2,21],133:[2,21],134:[2,21],135:[2,21],136:[2,21]},{1:[2,22],6:[2,22],25:[2,22],26:[2,22],49:[2,22],54:[2,22],57:[2,22],72:[2,22],77:[2,22],85:[2,22],90:[2,22],92:[2,22],101:[2,22],103:[2,22],104:[2,22],105:[2,22],109:[2,22],117:[2,22],125:[2,22],127:[2,22],128:[2,22],131:[2,22],132:[2,22],133:[2,22],134:[2,22],135:[2,22],136:[2,22]},{1:[2,23],6:[2,23],25:[2,23],26:[2,23],49:[2,23],54:[2,23],57:[2,23],72:[2,23],77:[2,23],85:[2,23],90:[2,23],92:[2,23],101:[2,23],103:[2,23],104:[2,23],105:[2,23],109:[2,23],117:[2,23],125:[2,23],127:[2,23],128:[2,23],131:[2,23],132:[2,23],133:[2,23],134:[2,23],135:[2,23],136:[2,23]},{1:[2,9],6:[2,9],26:[2,9],101:[2,9],103:[2,9],105:[2,9],109:[2,9],125:[2,9]},{1:[2,10],6:[2,10],26:[2,10],101:[2,10],103:[2,10],105:[2,10],109:[2,10],125:[2,10]},{1:[2,11],6:[2,11],26:[2,11],101:[2,11],103:[2,11],105:[2,11],109:[2,11],125:[2,11]},{1:[2,75],6:[2,75],25:[2,75],26:[2,75],40:[1,103],49:[2,75],54:[2,75],57:[2,75],66:[2,75],67:[2,75],68:[2,75],70:[2,75],72:[2,75],73:[2,75],77:[2,75],83:[2,75],84:[2,75],85:[2,75],90:[2,75],92:[2,75],101:[2,75],103:[2,75],104:[2,75],105:[2,75],109:[2,75],117:[2,75],125:[2,75],127:[2,75],128:[2,75],131:[2,75],132:[2,75],133:[2,75],134:[2,75],135:[2,75],136:[2,75]},{1:[2,76],6:[2,76],25:[2,76],26:[2,76],49:[2,76],54:[2,76],57:[2,76],66:[2,76],67:[2,76],68:[2,76],70:[2,76],72:[2,76],73:[2,76],77:[2,76],83:[2,76],84:[2,76],85:[2,76],90:[2,76],92:[2,76],101:[2,76],103:[2,76],104:[2,76],105:[2,76],109:[2,76],117:[2,76],125:[2,76],127:[2,76],128:[2,76],131:[2,76],132:[2,76],133:[2,76],134:[2,76],135:[2,76],136:[2,76]},{1:[2,77],6:[2,77],25:[2,77],26:[2,77],49:[2,77],54:[2,77],57:[2,77],66:[2,77],67:[2,77],68:[2,77],70:[2,77],72:[2,77],73:[2,77],77:[2,77],83:[2,77],84:[2,77],85:[2,77],90:[2,77],92:[2,77],101:[2,77],103:[2,77],104:[2,77],105:[2,77],109:[2,77],117:[2,77],125:[2,77],127:[2,77],128:[2,77],131:[2,77],132:[2,77],133:[2,77],134:[2,77],135:[2,77],136:[2,77]},{1:[2,78],6:[2,78],25:[2,78],26:[2,78],49:[2,78],54:[2,78],57:[2,78],66:[2,78],67:[2,78],68:[2,78],70:[2,78],72:[2,78],73:[2,78],77:[2,78],83:[2,78],84:[2,78],85:[2,78],90:[2,78],92:[2,78],101:[2,78],103:[2,78],104:[2,78],105:[2,78],109:[2,78],117:[2,78],125:[2,78],127:[2,78],128:[2,78],131:[2,78],132:[2,78],133:[2,78],134:[2,78],135:[2,78],136:[2,78]},{1:[2,79],6:[2,79],25:[2,79],26:[2,79],49:[2,79],54:[2,79],57:[2,79],66:[2,79],67:[2,79],68:[2,79],70:[2,79],72:[2,79],73:[2,79],77:[2,79],83:[2,79],84:[2,79],85:[2,79],90:[2,79],92:[2,79],101:[2,79],103:[2,79],104:[2,79],105:[2,79],109:[2,79],117:[2,79],125:[2,79],127:[2,79],128:[2,79],131:[2,79],132:[2,79],133:[2,79],134:[2,79],135:[2,79],136:[2,79]},{1:[2,105],6:[2,105],25:[2,105],26:[2,105],49:[2,105],54:[2,105],57:[2,105],66:[2,105],67:[2,105],68:[2,105],70:[2,105],72:[2,105],73:[2,105],77:[2,105],81:104,83:[2,105],84:[1,105],85:[2,105],90:[2,105],92:[2,105],101:[2,105],103:[2,105],104:[2,105],105:[2,105],109:[2,105],117:[2,105],125:[2,105],127:[2,105],128:[2,105],131:[2,105],132:[2,105],133:[2,105],134:[2,105],135:[2,105],136:[2,105]},{6:[2,55],25:[2,55],27:109,28:[1,73],44:110,48:106,49:[2,55],54:[2,55],55:107,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{5:115,25:[1,5]},{8:116,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:118,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:119,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{13:121,14:122,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:123,44:63,58:47,59:48,61:120,63:25,64:26,65:27,75:[1,70],82:[1,28],87:[1,58],88:[1,59],89:[1,57],100:[1,56]},{13:121,14:122,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:123,44:63,58:47,59:48,61:124,63:25,64:26,65:27,75:[1,70],82:[1,28],87:[1,58],88:[1,59],89:[1,57],100:[1,56]},{1:[2,72],6:[2,72],25:[2,72],26:[2,72],40:[2,72],49:[2,72],54:[2,72],57:[2,72],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,72],73:[2,72],77:[2,72],79:[1,128],83:[2,72],84:[2,72],85:[2,72],90:[2,72],92:[2,72],101:[2,72],103:[2,72],104:[2,72],105:[2,72],109:[2,72],117:[2,72],125:[2,72],127:[2,72],128:[2,72],129:[1,125],130:[1,126],131:[2,72],132:[2,72],133:[2,72],134:[2,72],135:[2,72],136:[2,72],137:[1,127]},{1:[2,180],6:[2,180],25:[2,180],26:[2,180],49:[2,180],54:[2,180],57:[2,180],72:[2,180],77:[2,180],85:[2,180],90:[2,180],92:[2,180],101:[2,180],103:[2,180],104:[2,180],105:[2,180],109:[2,180],117:[2,180],120:[1,129],125:[2,180],127:[2,180],128:[2,180],131:[2,180],132:[2,180],133:[2,180],134:[2,180],135:[2,180],136:[2,180]},{5:130,25:[1,5]},{5:131,25:[1,5]},{1:[2,147],6:[2,147],25:[2,147],26:[2,147],49:[2,147],54:[2,147],57:[2,147],72:[2,147],77:[2,147],85:[2,147],90:[2,147],92:[2,147],101:[2,147],103:[2,147],104:[2,147],105:[2,147],109:[2,147],117:[2,147],125:[2,147],127:[2,147],128:[2,147],131:[2,147],132:[2,147],133:[2,147],134:[2,147],135:[2,147],136:[2,147]},{5:132,25:[1,5]},{8:133,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,134],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,95],5:135,6:[2,95],13:121,14:122,25:[1,5],26:[2,95],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:123,44:63,49:[2,95],54:[2,95],57:[2,95],58:47,59:48,61:137,63:25,64:26,65:27,72:[2,95],75:[1,70],77:[2,95],79:[1,136],82:[1,28],85:[2,95],87:[1,58],88:[1,59],89:[1,57],90:[2,95],92:[2,95],100:[1,56],101:[2,95],103:[2,95],104:[2,95],105:[2,95],109:[2,95],117:[2,95],125:[2,95],127:[2,95],128:[2,95],131:[2,95],132:[2,95],133:[2,95],134:[2,95],135:[2,95],136:[2,95]},{8:138,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,47],6:[2,47],8:139,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,26:[2,47],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],101:[2,47],102:39,103:[2,47],105:[2,47],106:40,107:[1,67],108:41,109:[2,47],110:69,118:[1,42],123:37,124:[1,64],125:[2,47],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,48],6:[2,48],25:[2,48],26:[2,48],54:[2,48],77:[2,48],101:[2,48],103:[2,48],105:[2,48],109:[2,48],125:[2,48]},{1:[2,73],6:[2,73],25:[2,73],26:[2,73],40:[2,73],49:[2,73],54:[2,73],57:[2,73],66:[2,73],67:[2,73],68:[2,73],70:[2,73],72:[2,73],73:[2,73],77:[2,73],83:[2,73],84:[2,73],85:[2,73],90:[2,73],92:[2,73],101:[2,73],103:[2,73],104:[2,73],105:[2,73],109:[2,73],117:[2,73],125:[2,73],127:[2,73],128:[2,73],131:[2,73],132:[2,73],133:[2,73],134:[2,73],135:[2,73],136:[2,73]},{1:[2,74],6:[2,74],25:[2,74],26:[2,74],40:[2,74],49:[2,74],54:[2,74],57:[2,74],66:[2,74],67:[2,74],68:[2,74],70:[2,74],72:[2,74],73:[2,74],77:[2,74],83:[2,74],84:[2,74],85:[2,74],90:[2,74],92:[2,74],101:[2,74],103:[2,74],104:[2,74],105:[2,74],109:[2,74],117:[2,74],125:[2,74],127:[2,74],128:[2,74],131:[2,74],132:[2,74],133:[2,74],134:[2,74],135:[2,74],136:[2,74]},{1:[2,29],6:[2,29],25:[2,29],26:[2,29],49:[2,29],54:[2,29],57:[2,29],66:[2,29],67:[2,29],68:[2,29],70:[2,29],72:[2,29],73:[2,29],77:[2,29],83:[2,29],84:[2,29],85:[2,29],90:[2,29],92:[2,29],101:[2,29],103:[2,29],104:[2,29],105:[2,29],109:[2,29],117:[2,29],125:[2,29],127:[2,29],128:[2,29],131:[2,29],132:[2,29],133:[2,29],134:[2,29],135:[2,29],136:[2,29]},{1:[2,30],6:[2,30],25:[2,30],26:[2,30],49:[2,30],54:[2,30],57:[2,30],66:[2,30],67:[2,30],68:[2,30],70:[2,30],72:[2,30],73:[2,30],77:[2,30],83:[2,30],84:[2,30],85:[2,30],90:[2,30],92:[2,30],101:[2,30],103:[2,30],104:[2,30],105:[2,30],109:[2,30],117:[2,30],125:[2,30],127:[2,30],128:[2,30],131:[2,30],132:[2,30],133:[2,30],134:[2,30],135:[2,30],136:[2,30]},{1:[2,31],6:[2,31],25:[2,31],26:[2,31],49:[2,31],54:[2,31],57:[2,31],66:[2,31],67:[2,31],68:[2,31],70:[2,31],72:[2,31],73:[2,31],77:[2,31],83:[2,31],84:[2,31],85:[2,31],90:[2,31],92:[2,31],101:[2,31],103:[2,31],104:[2,31],105:[2,31],109:[2,31],117:[2,31],125:[2,31],127:[2,31],128:[2,31],131:[2,31],132:[2,31],133:[2,31],134:[2,31],135:[2,31],136:[2,31]},{1:[2,32],6:[2,32],25:[2,32],26:[2,32],49:[2,32],54:[2,32],57:[2,32],66:[2,32],67:[2,32],68:[2,32],70:[2,32],72:[2,32],73:[2,32],77:[2,32],83:[2,32],84:[2,32],85:[2,32],90:[2,32],92:[2,32],101:[2,32],103:[2,32],104:[2,32],105:[2,32],109:[2,32],117:[2,32],125:[2,32],127:[2,32],128:[2,32],131:[2,32],132:[2,32],133:[2,32],134:[2,32],135:[2,32],136:[2,32]},{1:[2,33],6:[2,33],25:[2,33],26:[2,33],49:[2,33],54:[2,33],57:[2,33],66:[2,33],67:[2,33],68:[2,33],70:[2,33],72:[2,33],73:[2,33],77:[2,33],83:[2,33],84:[2,33],85:[2,33],90:[2,33],92:[2,33],101:[2,33],103:[2,33],104:[2,33],105:[2,33],109:[2,33],117:[2,33],125:[2,33],127:[2,33],128:[2,33],131:[2,33],132:[2,33],133:[2,33],134:[2,33],135:[2,33],136:[2,33]},{1:[2,34],6:[2,34],25:[2,34],26:[2,34],49:[2,34],54:[2,34],57:[2,34],66:[2,34],67:[2,34],68:[2,34],70:[2,34],72:[2,34],73:[2,34],77:[2,34],83:[2,34],84:[2,34],85:[2,34],90:[2,34],92:[2,34],101:[2,34],103:[2,34],104:[2,34],105:[2,34],109:[2,34],117:[2,34],125:[2,34],127:[2,34],128:[2,34],131:[2,34],132:[2,34],133:[2,34],134:[2,34],135:[2,34],136:[2,34]},{1:[2,35],6:[2,35],25:[2,35],26:[2,35],49:[2,35],54:[2,35],57:[2,35],66:[2,35],67:[2,35],68:[2,35],70:[2,35],72:[2,35],73:[2,35],77:[2,35],83:[2,35],84:[2,35],85:[2,35],90:[2,35],92:[2,35],101:[2,35],103:[2,35],104:[2,35],105:[2,35],109:[2,35],117:[2,35],125:[2,35],127:[2,35],128:[2,35],131:[2,35],132:[2,35],133:[2,35],134:[2,35],135:[2,35],136:[2,35]},{4:140,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,141],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:142,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:144,87:[1,58],88:[1,59],89:[1,57],90:[1,143],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,111],6:[2,111],25:[2,111],26:[2,111],49:[2,111],54:[2,111],57:[2,111],66:[2,111],67:[2,111],68:[2,111],70:[2,111],72:[2,111],73:[2,111],77:[2,111],83:[2,111],84:[2,111],85:[2,111],90:[2,111],92:[2,111],101:[2,111],103:[2,111],104:[2,111],105:[2,111],109:[2,111],117:[2,111],125:[2,111],127:[2,111],128:[2,111],131:[2,111],132:[2,111],133:[2,111],134:[2,111],135:[2,111],136:[2,111]},{1:[2,112],6:[2,112],25:[2,112],26:[2,112],27:148,28:[1,73],49:[2,112],54:[2,112],57:[2,112],66:[2,112],67:[2,112],68:[2,112],70:[2,112],72:[2,112],73:[2,112],77:[2,112],83:[2,112],84:[2,112],85:[2,112],90:[2,112],92:[2,112],101:[2,112],103:[2,112],104:[2,112],105:[2,112],109:[2,112],117:[2,112],125:[2,112],127:[2,112],128:[2,112],131:[2,112],132:[2,112],133:[2,112],134:[2,112],135:[2,112],136:[2,112]},{25:[2,51]},{25:[2,52]},{1:[2,68],6:[2,68],25:[2,68],26:[2,68],40:[2,68],49:[2,68],54:[2,68],57:[2,68],66:[2,68],67:[2,68],68:[2,68],70:[2,68],72:[2,68],73:[2,68],77:[2,68],79:[2,68],83:[2,68],84:[2,68],85:[2,68],90:[2,68],92:[2,68],101:[2,68],103:[2,68],104:[2,68],105:[2,68],109:[2,68],117:[2,68],125:[2,68],127:[2,68],128:[2,68],129:[2,68],130:[2,68],131:[2,68],132:[2,68],133:[2,68],134:[2,68],135:[2,68],136:[2,68],137:[2,68]},{1:[2,71],6:[2,71],25:[2,71],26:[2,71],40:[2,71],49:[2,71],54:[2,71],57:[2,71],66:[2,71],67:[2,71],68:[2,71],70:[2,71],72:[2,71],73:[2,71],77:[2,71],79:[2,71],83:[2,71],84:[2,71],85:[2,71],90:[2,71],92:[2,71],101:[2,71],103:[2,71],104:[2,71],105:[2,71],109:[2,71],117:[2,71],125:[2,71],127:[2,71],128:[2,71],129:[2,71],130:[2,71],131:[2,71],132:[2,71],133:[2,71],134:[2,71],135:[2,71],136:[2,71],137:[2,71]},{8:149,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:150,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:151,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{5:152,8:153,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,5],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{27:158,28:[1,73],44:159,58:160,59:161,64:154,75:[1,70],88:[1,113],89:[1,57],112:155,113:[1,156],114:157},{111:162,115:[1,163],116:[1,164]},{6:[2,90],11:168,25:[2,90],27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:166,42:167,44:171,46:[1,46],54:[2,90],76:165,77:[2,90],88:[1,113]},{1:[2,27],6:[2,27],25:[2,27],26:[2,27],43:[2,27],49:[2,27],54:[2,27],57:[2,27],66:[2,27],67:[2,27],68:[2,27],70:[2,27],72:[2,27],73:[2,27],77:[2,27],83:[2,27],84:[2,27],85:[2,27],90:[2,27],92:[2,27],101:[2,27],103:[2,27],104:[2,27],105:[2,27],109:[2,27],117:[2,27],125:[2,27],127:[2,27],128:[2,27],131:[2,27],132:[2,27],133:[2,27],134:[2,27],135:[2,27],136:[2,27]},{1:[2,28],6:[2,28],25:[2,28],26:[2,28],43:[2,28],49:[2,28],54:[2,28],57:[2,28],66:[2,28],67:[2,28],68:[2,28],70:[2,28],72:[2,28],73:[2,28],77:[2,28],83:[2,28],84:[2,28],85:[2,28],90:[2,28],92:[2,28],101:[2,28],103:[2,28],104:[2,28],105:[2,28],109:[2,28],117:[2,28],125:[2,28],127:[2,28],128:[2,28],131:[2,28],132:[2,28],133:[2,28],134:[2,28],135:[2,28],136:[2,28]},{1:[2,26],6:[2,26],25:[2,26],26:[2,26],40:[2,26],43:[2,26],49:[2,26],54:[2,26],57:[2,26],66:[2,26],67:[2,26],68:[2,26],70:[2,26],72:[2,26],73:[2,26],77:[2,26],79:[2,26],83:[2,26],84:[2,26],85:[2,26],90:[2,26],92:[2,26],101:[2,26],103:[2,26],104:[2,26],105:[2,26],109:[2,26],115:[2,26],116:[2,26],117:[2,26],125:[2,26],127:[2,26],128:[2,26],129:[2,26],130:[2,26],131:[2,26],132:[2,26],133:[2,26],134:[2,26],135:[2,26],136:[2,26],137:[2,26]},{1:[2,6],6:[2,6],7:172,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,26:[2,6],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],101:[2,6],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,3]},{1:[2,24],6:[2,24],25:[2,24],26:[2,24],49:[2,24],54:[2,24],57:[2,24],72:[2,24],77:[2,24],85:[2,24],90:[2,24],92:[2,24],97:[2,24],98:[2,24],101:[2,24],103:[2,24],104:[2,24],105:[2,24],109:[2,24],117:[2,24],120:[2,24],122:[2,24],125:[2,24],127:[2,24],128:[2,24],131:[2,24],132:[2,24],133:[2,24],134:[2,24],135:[2,24],136:[2,24]},{6:[1,74],26:[1,173]},{1:[2,191],6:[2,191],25:[2,191],26:[2,191],49:[2,191],54:[2,191],57:[2,191],72:[2,191],77:[2,191],85:[2,191],90:[2,191],92:[2,191],101:[2,191],103:[2,191],104:[2,191],105:[2,191],109:[2,191],117:[2,191],125:[2,191],127:[2,191],128:[2,191],131:[2,191],132:[2,191],133:[2,191],134:[2,191],135:[2,191],136:[2,191]},{8:174,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:175,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:176,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:177,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:178,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:179,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:180,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:181,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,146],6:[2,146],25:[2,146],26:[2,146],49:[2,146],54:[2,146],57:[2,146],72:[2,146],77:[2,146],85:[2,146],90:[2,146],92:[2,146],101:[2,146],103:[2,146],104:[2,146],105:[2,146],109:[2,146],117:[2,146],125:[2,146],127:[2,146],128:[2,146],131:[2,146],132:[2,146],133:[2,146],134:[2,146],135:[2,146],136:[2,146]},{1:[2,151],6:[2,151],25:[2,151],26:[2,151],49:[2,151],54:[2,151],57:[2,151],72:[2,151],77:[2,151],85:[2,151],90:[2,151],92:[2,151],101:[2,151],103:[2,151],104:[2,151],105:[2,151],109:[2,151],117:[2,151],125:[2,151],127:[2,151],128:[2,151],131:[2,151],132:[2,151],133:[2,151],134:[2,151],135:[2,151],136:[2,151]},{8:182,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,145],6:[2,145],25:[2,145],26:[2,145],49:[2,145],54:[2,145],57:[2,145],72:[2,145],77:[2,145],85:[2,145],90:[2,145],92:[2,145],101:[2,145],103:[2,145],104:[2,145],105:[2,145],109:[2,145],117:[2,145],125:[2,145],127:[2,145],128:[2,145],131:[2,145],132:[2,145],133:[2,145],134:[2,145],135:[2,145],136:[2,145]},{1:[2,150],6:[2,150],25:[2,150],26:[2,150],49:[2,150],54:[2,150],57:[2,150],72:[2,150],77:[2,150],85:[2,150],90:[2,150],92:[2,150],101:[2,150],103:[2,150],104:[2,150],105:[2,150],109:[2,150],117:[2,150],125:[2,150],127:[2,150],128:[2,150],131:[2,150],132:[2,150],133:[2,150],134:[2,150],135:[2,150],136:[2,150]},{81:183,84:[1,105]},{1:[2,69],6:[2,69],25:[2,69],26:[2,69],40:[2,69],49:[2,69],54:[2,69],57:[2,69],66:[2,69],67:[2,69],68:[2,69],70:[2,69],72:[2,69],73:[2,69],77:[2,69],79:[2,69],83:[2,69],84:[2,69],85:[2,69],90:[2,69],92:[2,69],101:[2,69],103:[2,69],104:[2,69],105:[2,69],109:[2,69],117:[2,69],125:[2,69],127:[2,69],128:[2,69],129:[2,69],130:[2,69],131:[2,69],132:[2,69],133:[2,69],134:[2,69],135:[2,69],136:[2,69],137:[2,69]},{84:[2,108]},{27:184,28:[1,73]},{27:185,28:[1,73]},{1:[2,83],6:[2,83],25:[2,83],26:[2,83],27:186,28:[1,73],40:[2,83],49:[2,83],54:[2,83],57:[2,83],66:[2,83],67:[2,83],68:[2,83],70:[2,83],72:[2,83],73:[2,83],77:[2,83],79:[2,83],83:[2,83],84:[2,83],85:[2,83],90:[2,83],92:[2,83],101:[2,83],103:[2,83],104:[2,83],105:[2,83],109:[2,83],117:[2,83],125:[2,83],127:[2,83],128:[2,83],129:[2,83],130:[2,83],131:[2,83],132:[2,83],133:[2,83],134:[2,83],135:[2,83],136:[2,83],137:[2,83]},{1:[2,84],6:[2,84],25:[2,84],26:[2,84],40:[2,84],49:[2,84],54:[2,84],57:[2,84],66:[2,84],67:[2,84],68:[2,84],70:[2,84],72:[2,84],73:[2,84],77:[2,84],79:[2,84],83:[2,84],84:[2,84],85:[2,84],90:[2,84],92:[2,84],101:[2,84],103:[2,84],104:[2,84],105:[2,84],109:[2,84],117:[2,84],125:[2,84],127:[2,84],128:[2,84],129:[2,84],130:[2,84],131:[2,84],132:[2,84],133:[2,84],134:[2,84],135:[2,84],136:[2,84],137:[2,84]},{8:188,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],57:[1,192],58:47,59:48,61:36,63:25,64:26,65:27,71:187,74:189,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],91:190,92:[1,191],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{69:193,70:[1,99],73:[1,100]},{81:194,84:[1,105]},{1:[2,70],6:[2,70],25:[2,70],26:[2,70],40:[2,70],49:[2,70],54:[2,70],57:[2,70],66:[2,70],67:[2,70],68:[2,70],70:[2,70],72:[2,70],73:[2,70],77:[2,70],79:[2,70],83:[2,70],84:[2,70],85:[2,70],90:[2,70],92:[2,70],101:[2,70],103:[2,70],104:[2,70],105:[2,70],109:[2,70],117:[2,70],125:[2,70],127:[2,70],128:[2,70],129:[2,70],130:[2,70],131:[2,70],132:[2,70],133:[2,70],134:[2,70],135:[2,70],136:[2,70],137:[2,70]},{6:[1,196],8:195,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,197],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,106],6:[2,106],25:[2,106],26:[2,106],49:[2,106],54:[2,106],57:[2,106],66:[2,106],67:[2,106],68:[2,106],70:[2,106],72:[2,106],73:[2,106],77:[2,106],83:[2,106],84:[2,106],85:[2,106],90:[2,106],92:[2,106],101:[2,106],103:[2,106],104:[2,106],105:[2,106],109:[2,106],117:[2,106],125:[2,106],127:[2,106],128:[2,106],131:[2,106],132:[2,106],133:[2,106],134:[2,106],135:[2,106],136:[2,106]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],85:[1,198],86:199,87:[1,58],88:[1,59],89:[1,57],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,53],25:[2,53],49:[1,201],53:203,54:[1,202]},{6:[2,56],25:[2,56],26:[2,56],49:[2,56],54:[2,56]},{6:[2,60],25:[2,60],26:[2,60],40:[1,205],49:[2,60],54:[2,60],57:[1,204]},{6:[2,63],25:[2,63],26:[2,63],40:[2,63],49:[2,63],54:[2,63],57:[2,63]},{6:[2,64],25:[2,64],26:[2,64],40:[2,64],49:[2,64],54:[2,64],57:[2,64]},{6:[2,65],25:[2,65],26:[2,65],40:[2,65],49:[2,65],54:[2,65],57:[2,65]},{6:[2,66],25:[2,66],26:[2,66],40:[2,66],49:[2,66],54:[2,66],57:[2,66]},{27:148,28:[1,73]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:144,87:[1,58],88:[1,59],89:[1,57],90:[1,143],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,50],6:[2,50],25:[2,50],26:[2,50],49:[2,50],54:[2,50],57:[2,50],72:[2,50],77:[2,50],85:[2,50],90:[2,50],92:[2,50],101:[2,50],103:[2,50],104:[2,50],105:[2,50],109:[2,50],117:[2,50],125:[2,50],127:[2,50],128:[2,50],131:[2,50],132:[2,50],133:[2,50],134:[2,50],135:[2,50],136:[2,50]},{1:[2,184],6:[2,184],25:[2,184],26:[2,184],49:[2,184],54:[2,184],57:[2,184],72:[2,184],77:[2,184],85:[2,184],90:[2,184],92:[2,184],101:[2,184],102:87,103:[2,184],104:[2,184],105:[2,184],108:88,109:[2,184],110:69,117:[2,184],125:[2,184],127:[2,184],128:[2,184],131:[1,78],132:[2,184],133:[2,184],134:[2,184],135:[2,184],136:[2,184]},{102:90,103:[1,65],105:[1,66],108:91,109:[1,68],110:69,125:[1,89]},{1:[2,185],6:[2,185],25:[2,185],26:[2,185],49:[2,185],54:[2,185],57:[2,185],72:[2,185],77:[2,185],85:[2,185],90:[2,185],92:[2,185],101:[2,185],102:87,103:[2,185],104:[2,185],105:[2,185],108:88,109:[2,185],110:69,117:[2,185],125:[2,185],127:[2,185],128:[2,185],131:[1,78],132:[2,185],133:[2,185],134:[2,185],135:[2,185],136:[2,185]},{1:[2,186],6:[2,186],25:[2,186],26:[2,186],49:[2,186],54:[2,186],57:[2,186],72:[2,186],77:[2,186],85:[2,186],90:[2,186],92:[2,186],101:[2,186],102:87,103:[2,186],104:[2,186],105:[2,186],108:88,109:[2,186],110:69,117:[2,186],125:[2,186],127:[2,186],128:[2,186],131:[1,78],132:[2,186],133:[2,186],134:[2,186],135:[2,186],136:[2,186]},{1:[2,187],6:[2,187],25:[2,187],26:[2,187],49:[2,187],54:[2,187],57:[2,187],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,187],73:[2,72],77:[2,187],83:[2,72],84:[2,72],85:[2,187],90:[2,187],92:[2,187],101:[2,187],103:[2,187],104:[2,187],105:[2,187],109:[2,187],117:[2,187],125:[2,187],127:[2,187],128:[2,187],131:[2,187],132:[2,187],133:[2,187],134:[2,187],135:[2,187],136:[2,187]},{62:93,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],73:[1,100],80:92,83:[1,94],84:[2,107]},{62:102,66:[1,95],67:[1,96],68:[1,97],69:98,70:[1,99],73:[1,100],80:101,83:[1,94],84:[2,107]},{66:[2,75],67:[2,75],68:[2,75],70:[2,75],73:[2,75],83:[2,75],84:[2,75]},{1:[2,188],6:[2,188],25:[2,188],26:[2,188],49:[2,188],54:[2,188],57:[2,188],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,188],73:[2,72],77:[2,188],83:[2,72],84:[2,72],85:[2,188],90:[2,188],92:[2,188],101:[2,188],103:[2,188],104:[2,188],105:[2,188],109:[2,188],117:[2,188],125:[2,188],127:[2,188],128:[2,188],131:[2,188],132:[2,188],133:[2,188],134:[2,188],135:[2,188],136:[2,188]},{1:[2,189],6:[2,189],25:[2,189],26:[2,189],49:[2,189],54:[2,189],57:[2,189],72:[2,189],77:[2,189],85:[2,189],90:[2,189],92:[2,189],101:[2,189],103:[2,189],104:[2,189],105:[2,189],109:[2,189],117:[2,189],125:[2,189],127:[2,189],128:[2,189],131:[2,189],132:[2,189],133:[2,189],134:[2,189],135:[2,189],136:[2,189]},{1:[2,190],6:[2,190],25:[2,190],26:[2,190],49:[2,190],54:[2,190],57:[2,190],72:[2,190],77:[2,190],85:[2,190],90:[2,190],92:[2,190],101:[2,190],103:[2,190],104:[2,190],105:[2,190],109:[2,190],117:[2,190],125:[2,190],127:[2,190],128:[2,190],131:[2,190],132:[2,190],133:[2,190],134:[2,190],135:[2,190],136:[2,190]},{8:206,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,207],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:208,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{5:209,25:[1,5],124:[1,210]},{1:[2,132],6:[2,132],25:[2,132],26:[2,132],49:[2,132],54:[2,132],57:[2,132],72:[2,132],77:[2,132],85:[2,132],90:[2,132],92:[2,132],96:211,97:[1,212],98:[1,213],101:[2,132],103:[2,132],104:[2,132],105:[2,132],109:[2,132],117:[2,132],125:[2,132],127:[2,132],128:[2,132],131:[2,132],132:[2,132],133:[2,132],134:[2,132],135:[2,132],136:[2,132]},{1:[2,144],6:[2,144],25:[2,144],26:[2,144],49:[2,144],54:[2,144],57:[2,144],72:[2,144],77:[2,144],85:[2,144],90:[2,144],92:[2,144],101:[2,144],103:[2,144],104:[2,144],105:[2,144],109:[2,144],117:[2,144],125:[2,144],127:[2,144],128:[2,144],131:[2,144],132:[2,144],133:[2,144],134:[2,144],135:[2,144],136:[2,144]},{1:[2,152],6:[2,152],25:[2,152],26:[2,152],49:[2,152],54:[2,152],57:[2,152],72:[2,152],77:[2,152],85:[2,152],90:[2,152],92:[2,152],101:[2,152],103:[2,152],104:[2,152],105:[2,152],109:[2,152],117:[2,152],125:[2,152],127:[2,152],128:[2,152],131:[2,152],132:[2,152],133:[2,152],134:[2,152],135:[2,152],136:[2,152]},{25:[1,214],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{119:215,121:216,122:[1,217]},{1:[2,96],6:[2,96],25:[2,96],26:[2,96],49:[2,96],54:[2,96],57:[2,96],72:[2,96],77:[2,96],85:[2,96],90:[2,96],92:[2,96],101:[2,96],103:[2,96],104:[2,96],105:[2,96],109:[2,96],117:[2,96],125:[2,96],127:[2,96],128:[2,96],131:[2,96],132:[2,96],133:[2,96],134:[2,96],135:[2,96],136:[2,96]},{8:218,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,99],5:219,6:[2,99],25:[1,5],26:[2,99],49:[2,99],54:[2,99],57:[2,99],66:[2,72],67:[2,72],68:[2,72],70:[2,72],72:[2,99],73:[2,72],77:[2,99],79:[1,220],83:[2,72],84:[2,72],85:[2,99],90:[2,99],92:[2,99],101:[2,99],103:[2,99],104:[2,99],105:[2,99],109:[2,99],117:[2,99],125:[2,99],127:[2,99],128:[2,99],131:[2,99],132:[2,99],133:[2,99],134:[2,99],135:[2,99],136:[2,99]},{1:[2,137],6:[2,137],25:[2,137],26:[2,137],49:[2,137],54:[2,137],57:[2,137],72:[2,137],77:[2,137],85:[2,137],90:[2,137],92:[2,137],101:[2,137],102:87,103:[2,137],104:[2,137],105:[2,137],108:88,109:[2,137],110:69,117:[2,137],125:[2,137],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,46],6:[2,46],26:[2,46],101:[2,46],102:87,103:[2,46],105:[2,46],108:88,109:[2,46],110:69,125:[2,46],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,74],101:[1,221]},{4:222,7:4,8:6,9:7,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,128],25:[2,128],54:[2,128],57:[1,224],90:[2,128],91:223,92:[1,191],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,114],6:[2,114],25:[2,114],26:[2,114],40:[2,114],49:[2,114],54:[2,114],57:[2,114],66:[2,114],67:[2,114],68:[2,114],70:[2,114],72:[2,114],73:[2,114],77:[2,114],83:[2,114],84:[2,114],85:[2,114],90:[2,114],92:[2,114],101:[2,114],103:[2,114],104:[2,114],105:[2,114],109:[2,114],115:[2,114],116:[2,114],117:[2,114],125:[2,114],127:[2,114],128:[2,114],131:[2,114],132:[2,114],133:[2,114],134:[2,114],135:[2,114],136:[2,114]},{6:[2,53],25:[2,53],53:225,54:[1,226],90:[2,53]},{6:[2,123],25:[2,123],26:[2,123],54:[2,123],85:[2,123],90:[2,123]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:227,87:[1,58],88:[1,59],89:[1,57],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,129],25:[2,129],26:[2,129],54:[2,129],85:[2,129],90:[2,129]},{1:[2,113],6:[2,113],25:[2,113],26:[2,113],40:[2,113],43:[2,113],49:[2,113],54:[2,113],57:[2,113],66:[2,113],67:[2,113],68:[2,113],70:[2,113],72:[2,113],73:[2,113],77:[2,113],79:[2,113],83:[2,113],84:[2,113],85:[2,113],90:[2,113],92:[2,113],101:[2,113],103:[2,113],104:[2,113],105:[2,113],109:[2,113],115:[2,113],116:[2,113],117:[2,113],125:[2,113],127:[2,113],128:[2,113],129:[2,113],130:[2,113],131:[2,113],132:[2,113],133:[2,113],134:[2,113],135:[2,113],136:[2,113],137:[2,113]},{5:228,25:[1,5],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,140],6:[2,140],25:[2,140],26:[2,140],49:[2,140],54:[2,140],57:[2,140],72:[2,140],77:[2,140],85:[2,140],90:[2,140],92:[2,140],101:[2,140],102:87,103:[1,65],104:[1,229],105:[1,66],108:88,109:[1,68],110:69,117:[2,140],125:[2,140],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,142],6:[2,142],25:[2,142],26:[2,142],49:[2,142],54:[2,142],57:[2,142],72:[2,142],77:[2,142],85:[2,142],90:[2,142],92:[2,142],101:[2,142],102:87,103:[1,65],104:[1,230],105:[1,66],108:88,109:[1,68],110:69,117:[2,142],125:[2,142],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,148],6:[2,148],25:[2,148],26:[2,148],49:[2,148],54:[2,148],57:[2,148],72:[2,148],77:[2,148],85:[2,148],90:[2,148],92:[2,148],101:[2,148],103:[2,148],104:[2,148],105:[2,148],109:[2,148],117:[2,148],125:[2,148],127:[2,148],128:[2,148],131:[2,148],132:[2,148],133:[2,148],134:[2,148],135:[2,148],136:[2,148]},{1:[2,149],6:[2,149],25:[2,149],26:[2,149],49:[2,149],54:[2,149],57:[2,149],72:[2,149],77:[2,149],85:[2,149],90:[2,149],92:[2,149],101:[2,149],102:87,103:[1,65],104:[2,149],105:[1,66],108:88,109:[1,68],110:69,117:[2,149],125:[2,149],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,153],6:[2,153],25:[2,153],26:[2,153],49:[2,153],54:[2,153],57:[2,153],72:[2,153],77:[2,153],85:[2,153],90:[2,153],92:[2,153],101:[2,153],103:[2,153],104:[2,153],105:[2,153],109:[2,153],117:[2,153],125:[2,153],127:[2,153],128:[2,153],131:[2,153],132:[2,153],133:[2,153],134:[2,153],135:[2,153],136:[2,153]},{115:[2,155],116:[2,155]},{27:158,28:[1,73],44:159,58:160,59:161,75:[1,70],88:[1,113],89:[1,114],112:231,114:157},{54:[1,232],115:[2,161],116:[2,161]},{54:[2,157],115:[2,157],116:[2,157]},{54:[2,158],115:[2,158],116:[2,158]},{54:[2,159],115:[2,159],116:[2,159]},{54:[2,160],115:[2,160],116:[2,160]},{1:[2,154],6:[2,154],25:[2,154],26:[2,154],49:[2,154],54:[2,154],57:[2,154],72:[2,154],77:[2,154],85:[2,154],90:[2,154],92:[2,154],101:[2,154],103:[2,154],104:[2,154],105:[2,154],109:[2,154],117:[2,154],125:[2,154],127:[2,154],128:[2,154],131:[2,154],132:[2,154],133:[2,154],134:[2,154],135:[2,154],136:[2,154]},{8:233,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:234,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,53],25:[2,53],53:235,54:[1,236],77:[2,53]},{6:[2,91],25:[2,91],26:[2,91],54:[2,91],77:[2,91]},{6:[2,39],25:[2,39],26:[2,39],43:[1,237],54:[2,39],77:[2,39]},{6:[2,42],25:[2,42],26:[2,42],54:[2,42],77:[2,42]},{6:[2,43],25:[2,43],26:[2,43],43:[2,43],54:[2,43],77:[2,43]},{6:[2,44],25:[2,44],26:[2,44],43:[2,44],54:[2,44],77:[2,44]},{6:[2,45],25:[2,45],26:[2,45],43:[2,45],54:[2,45],77:[2,45]},{1:[2,5],6:[2,5],26:[2,5],101:[2,5]},{1:[2,25],6:[2,25],25:[2,25],26:[2,25],49:[2,25],54:[2,25],57:[2,25],72:[2,25],77:[2,25],85:[2,25],90:[2,25],92:[2,25],97:[2,25],98:[2,25],101:[2,25],103:[2,25],104:[2,25],105:[2,25],109:[2,25],117:[2,25],120:[2,25],122:[2,25],125:[2,25],127:[2,25],128:[2,25],131:[2,25],132:[2,25],133:[2,25],134:[2,25],135:[2,25],136:[2,25]},{1:[2,192],6:[2,192],25:[2,192],26:[2,192],49:[2,192],54:[2,192],57:[2,192],72:[2,192],77:[2,192],85:[2,192],90:[2,192],92:[2,192],101:[2,192],102:87,103:[2,192],104:[2,192],105:[2,192],108:88,109:[2,192],110:69,117:[2,192],125:[2,192],127:[2,192],128:[2,192],131:[1,78],132:[1,81],133:[2,192],134:[2,192],135:[2,192],136:[2,192]},{1:[2,193],6:[2,193],25:[2,193],26:[2,193],49:[2,193],54:[2,193],57:[2,193],72:[2,193],77:[2,193],85:[2,193],90:[2,193],92:[2,193],101:[2,193],102:87,103:[2,193],104:[2,193],105:[2,193],108:88,109:[2,193],110:69,117:[2,193],125:[2,193],127:[2,193],128:[2,193],131:[1,78],132:[1,81],133:[2,193],134:[2,193],135:[2,193],136:[2,193]},{1:[2,194],6:[2,194],25:[2,194],26:[2,194],49:[2,194],54:[2,194],57:[2,194],72:[2,194],77:[2,194],85:[2,194],90:[2,194],92:[2,194],101:[2,194],102:87,103:[2,194],104:[2,194],105:[2,194],108:88,109:[2,194],110:69,117:[2,194],125:[2,194],127:[2,194],128:[2,194],131:[1,78],132:[2,194],133:[2,194],134:[2,194],135:[2,194],136:[2,194]},{1:[2,195],6:[2,195],25:[2,195],26:[2,195],49:[2,195],54:[2,195],57:[2,195],72:[2,195],77:[2,195],85:[2,195],90:[2,195],92:[2,195],101:[2,195],102:87,103:[2,195],104:[2,195],105:[2,195],108:88,109:[2,195],110:69,117:[2,195],125:[2,195],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[2,195],134:[2,195],135:[2,195],136:[2,195]},{1:[2,196],6:[2,196],25:[2,196],26:[2,196],49:[2,196],54:[2,196],57:[2,196],72:[2,196],77:[2,196],85:[2,196],90:[2,196],92:[2,196],101:[2,196],102:87,103:[2,196],104:[2,196],105:[2,196],108:88,109:[2,196],110:69,117:[2,196],125:[2,196],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[2,196],135:[2,196],136:[1,85]},{1:[2,197],6:[2,197],25:[2,197],26:[2,197],49:[2,197],54:[2,197],57:[2,197],72:[2,197],77:[2,197],85:[2,197],90:[2,197],92:[2,197],101:[2,197],102:87,103:[2,197],104:[2,197],105:[2,197],108:88,109:[2,197],110:69,117:[2,197],125:[2,197],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[2,197],136:[1,85]},{1:[2,198],6:[2,198],25:[2,198],26:[2,198],49:[2,198],54:[2,198],57:[2,198],72:[2,198],77:[2,198],85:[2,198],90:[2,198],92:[2,198],101:[2,198],102:87,103:[2,198],104:[2,198],105:[2,198],108:88,109:[2,198],110:69,117:[2,198],125:[2,198],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[2,198],135:[2,198],136:[2,198]},{1:[2,183],6:[2,183],25:[2,183],26:[2,183],49:[2,183],54:[2,183],57:[2,183],72:[2,183],77:[2,183],85:[2,183],90:[2,183],92:[2,183],101:[2,183],102:87,103:[1,65],104:[2,183],105:[1,66],108:88,109:[1,68],110:69,117:[2,183],125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,182],6:[2,182],25:[2,182],26:[2,182],49:[2,182],54:[2,182],57:[2,182],72:[2,182],77:[2,182],85:[2,182],90:[2,182],92:[2,182],101:[2,182],102:87,103:[1,65],104:[2,182],105:[1,66],108:88,109:[1,68],110:69,117:[2,182],125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,103],6:[2,103],25:[2,103],26:[2,103],49:[2,103],54:[2,103],57:[2,103],66:[2,103],67:[2,103],68:[2,103],70:[2,103],72:[2,103],73:[2,103],77:[2,103],83:[2,103],84:[2,103],85:[2,103],90:[2,103],92:[2,103],101:[2,103],103:[2,103],104:[2,103],105:[2,103],109:[2,103],117:[2,103],125:[2,103],127:[2,103],128:[2,103],131:[2,103],132:[2,103],133:[2,103],134:[2,103],135:[2,103],136:[2,103]},{1:[2,80],6:[2,80],25:[2,80],26:[2,80],40:[2,80],49:[2,80],54:[2,80],57:[2,80],66:[2,80],67:[2,80],68:[2,80],70:[2,80],72:[2,80],73:[2,80],77:[2,80],79:[2,80],83:[2,80],84:[2,80],85:[2,80],90:[2,80],92:[2,80],101:[2,80],103:[2,80],104:[2,80],105:[2,80],109:[2,80],117:[2,80],125:[2,80],127:[2,80],128:[2,80],129:[2,80],130:[2,80],131:[2,80],132:[2,80],133:[2,80],134:[2,80],135:[2,80],136:[2,80],137:[2,80]},{1:[2,81],6:[2,81],25:[2,81],26:[2,81],40:[2,81],49:[2,81],54:[2,81],57:[2,81],66:[2,81],67:[2,81],68:[2,81],70:[2,81],72:[2,81],73:[2,81],77:[2,81],79:[2,81],83:[2,81],84:[2,81],85:[2,81],90:[2,81],92:[2,81],101:[2,81],103:[2,81],104:[2,81],105:[2,81],109:[2,81],117:[2,81],125:[2,81],127:[2,81],128:[2,81],129:[2,81],130:[2,81],131:[2,81],132:[2,81],133:[2,81],134:[2,81],135:[2,81],136:[2,81],137:[2,81]},{1:[2,82],6:[2,82],25:[2,82],26:[2,82],40:[2,82],49:[2,82],54:[2,82],57:[2,82],66:[2,82],67:[2,82],68:[2,82],70:[2,82],72:[2,82],73:[2,82],77:[2,82],79:[2,82],83:[2,82],84:[2,82],85:[2,82],90:[2,82],92:[2,82],101:[2,82],103:[2,82],104:[2,82],105:[2,82],109:[2,82],117:[2,82],125:[2,82],127:[2,82],128:[2,82],129:[2,82],130:[2,82],131:[2,82],132:[2,82],133:[2,82],134:[2,82],135:[2,82],136:[2,82],137:[2,82]},{72:[1,238]},{57:[1,192],72:[2,87],91:239,92:[1,191],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{72:[2,88]},{8:240,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,72:[2,122],75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{12:[2,116],28:[2,116],30:[2,116],31:[2,116],33:[2,116],34:[2,116],35:[2,116],36:[2,116],37:[2,116],38:[2,116],45:[2,116],46:[2,116],47:[2,116],51:[2,116],52:[2,116],72:[2,116],75:[2,116],78:[2,116],82:[2,116],87:[2,116],88:[2,116],89:[2,116],95:[2,116],99:[2,116],100:[2,116],103:[2,116],105:[2,116],107:[2,116],109:[2,116],118:[2,116],124:[2,116],126:[2,116],127:[2,116],128:[2,116],129:[2,116],130:[2,116]},{12:[2,117],28:[2,117],30:[2,117],31:[2,117],33:[2,117],34:[2,117],35:[2,117],36:[2,117],37:[2,117],38:[2,117],45:[2,117],46:[2,117],47:[2,117],51:[2,117],52:[2,117],72:[2,117],75:[2,117],78:[2,117],82:[2,117],87:[2,117],88:[2,117],89:[2,117],95:[2,117],99:[2,117],100:[2,117],103:[2,117],105:[2,117],107:[2,117],109:[2,117],118:[2,117],124:[2,117],126:[2,117],127:[2,117],128:[2,117],129:[2,117],130:[2,117]},{1:[2,86],6:[2,86],25:[2,86],26:[2,86],40:[2,86],49:[2,86],54:[2,86],57:[2,86],66:[2,86],67:[2,86],68:[2,86],70:[2,86],72:[2,86],73:[2,86],77:[2,86],79:[2,86],83:[2,86],84:[2,86],85:[2,86],90:[2,86],92:[2,86],101:[2,86],103:[2,86],104:[2,86],105:[2,86],109:[2,86],117:[2,86],125:[2,86],127:[2,86],128:[2,86],129:[2,86],130:[2,86],131:[2,86],132:[2,86],133:[2,86],134:[2,86],135:[2,86],136:[2,86],137:[2,86]},{1:[2,104],6:[2,104],25:[2,104],26:[2,104],49:[2,104],54:[2,104],57:[2,104],66:[2,104],67:[2,104],68:[2,104],70:[2,104],72:[2,104],73:[2,104],77:[2,104],83:[2,104],84:[2,104],85:[2,104],90:[2,104],92:[2,104],101:[2,104],103:[2,104],104:[2,104],105:[2,104],109:[2,104],117:[2,104],125:[2,104],127:[2,104],128:[2,104],131:[2,104],132:[2,104],133:[2,104],134:[2,104],135:[2,104],136:[2,104]},{1:[2,36],6:[2,36],25:[2,36],26:[2,36],49:[2,36],54:[2,36],57:[2,36],72:[2,36],77:[2,36],85:[2,36],90:[2,36],92:[2,36],101:[2,36],102:87,103:[2,36],104:[2,36],105:[2,36],108:88,109:[2,36],110:69,117:[2,36],125:[2,36],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{8:241,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:242,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,109],6:[2,109],25:[2,109],26:[2,109],49:[2,109],54:[2,109],57:[2,109],66:[2,109],67:[2,109],68:[2,109],70:[2,109],72:[2,109],73:[2,109],77:[2,109],83:[2,109],84:[2,109],85:[2,109],90:[2,109],92:[2,109],101:[2,109],103:[2,109],104:[2,109],105:[2,109],109:[2,109],117:[2,109],125:[2,109],127:[2,109],128:[2,109],131:[2,109],132:[2,109],133:[2,109],134:[2,109],135:[2,109],136:[2,109]},{6:[2,53],25:[2,53],53:243,54:[1,226],85:[2,53]},{6:[2,128],25:[2,128],26:[2,128],54:[2,128],57:[1,244],85:[2,128],90:[2,128],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{50:245,51:[1,60],52:[1,61]},{6:[2,54],25:[2,54],26:[2,54],27:109,28:[1,73],44:110,55:246,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{6:[1,247],25:[1,248]},{6:[2,61],25:[2,61],26:[2,61],49:[2,61],54:[2,61]},{8:249,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,199],6:[2,199],25:[2,199],26:[2,199],49:[2,199],54:[2,199],57:[2,199],72:[2,199],77:[2,199],85:[2,199],90:[2,199],92:[2,199],101:[2,199],102:87,103:[2,199],104:[2,199],105:[2,199],108:88,109:[2,199],110:69,117:[2,199],125:[2,199],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{8:250,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,201],6:[2,201],25:[2,201],26:[2,201],49:[2,201],54:[2,201],57:[2,201],72:[2,201],77:[2,201],85:[2,201],90:[2,201],92:[2,201],101:[2,201],102:87,103:[2,201],104:[2,201],105:[2,201],108:88,109:[2,201],110:69,117:[2,201],125:[2,201],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,181],6:[2,181],25:[2,181],26:[2,181],49:[2,181],54:[2,181],57:[2,181],72:[2,181],77:[2,181],85:[2,181],90:[2,181],92:[2,181],101:[2,181],103:[2,181],104:[2,181],105:[2,181],109:[2,181],117:[2,181],125:[2,181],127:[2,181],128:[2,181],131:[2,181],132:[2,181],133:[2,181],134:[2,181],135:[2,181],136:[2,181]},{8:251,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,133],6:[2,133],25:[2,133],26:[2,133],49:[2,133],54:[2,133],57:[2,133],72:[2,133],77:[2,133],85:[2,133],90:[2,133],92:[2,133],97:[1,252],101:[2,133],103:[2,133],104:[2,133],105:[2,133],109:[2,133],117:[2,133],125:[2,133],127:[2,133],128:[2,133],131:[2,133],132:[2,133],133:[2,133],134:[2,133],135:[2,133],136:[2,133]},{5:253,25:[1,5]},{27:254,28:[1,73]},{119:255,121:216,122:[1,217]},{26:[1,256],120:[1,257],121:258,122:[1,217]},{26:[2,174],120:[2,174],122:[2,174]},{8:260,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],94:259,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,97],5:261,6:[2,97],25:[1,5],26:[2,97],49:[2,97],54:[2,97],57:[2,97],72:[2,97],77:[2,97],85:[2,97],90:[2,97],92:[2,97],101:[2,97],102:87,103:[1,65],104:[2,97],105:[1,66],108:88,109:[1,68],110:69,117:[2,97],125:[2,97],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,100],6:[2,100],25:[2,100],26:[2,100],49:[2,100],54:[2,100],57:[2,100],72:[2,100],77:[2,100],85:[2,100],90:[2,100],92:[2,100],101:[2,100],103:[2,100],104:[2,100],105:[2,100],109:[2,100],117:[2,100],125:[2,100],127:[2,100],128:[2,100],131:[2,100],132:[2,100],133:[2,100],134:[2,100],135:[2,100],136:[2,100]},{8:262,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,138],6:[2,138],25:[2,138],26:[2,138],49:[2,138],54:[2,138],57:[2,138],66:[2,138],67:[2,138],68:[2,138],70:[2,138],72:[2,138],73:[2,138],77:[2,138],83:[2,138],84:[2,138],85:[2,138],90:[2,138],92:[2,138],101:[2,138],103:[2,138],104:[2,138],105:[2,138],109:[2,138],117:[2,138],125:[2,138],127:[2,138],128:[2,138],131:[2,138],132:[2,138],133:[2,138],134:[2,138],135:[2,138],136:[2,138]},{6:[1,74],26:[1,263]},{8:264,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,67],12:[2,117],25:[2,67],28:[2,117],30:[2,117],31:[2,117],33:[2,117],34:[2,117],35:[2,117],36:[2,117],37:[2,117],38:[2,117],45:[2,117],46:[2,117],47:[2,117],51:[2,117],52:[2,117],54:[2,67],75:[2,117],78:[2,117],82:[2,117],87:[2,117],88:[2,117],89:[2,117],90:[2,67],95:[2,117],99:[2,117],100:[2,117],103:[2,117],105:[2,117],107:[2,117],109:[2,117],118:[2,117],124:[2,117],126:[2,117],127:[2,117],128:[2,117],129:[2,117],130:[2,117]},{6:[1,266],25:[1,267],90:[1,265]},{6:[2,54],8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[2,54],26:[2,54],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],85:[2,54],87:[1,58],88:[1,59],89:[1,57],90:[2,54],93:268,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,53],25:[2,53],26:[2,53],53:269,54:[1,226]},{1:[2,178],6:[2,178],25:[2,178],26:[2,178],49:[2,178],54:[2,178],57:[2,178],72:[2,178],77:[2,178],85:[2,178],90:[2,178],92:[2,178],101:[2,178],103:[2,178],104:[2,178],105:[2,178],109:[2,178],117:[2,178],120:[2,178],125:[2,178],127:[2,178],128:[2,178],131:[2,178],132:[2,178],133:[2,178],134:[2,178],135:[2,178],136:[2,178]},{8:270,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:271,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{115:[2,156],116:[2,156]},{27:158,28:[1,73],44:159,58:160,59:161,75:[1,70],88:[1,113],89:[1,114],114:272},{1:[2,163],6:[2,163],25:[2,163],26:[2,163],49:[2,163],54:[2,163],57:[2,163],72:[2,163],77:[2,163],85:[2,163],90:[2,163],92:[2,163],101:[2,163],102:87,103:[2,163],104:[1,273],105:[2,163],108:88,109:[2,163],110:69,117:[1,274],125:[2,163],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,164],6:[2,164],25:[2,164],26:[2,164],49:[2,164],54:[2,164],57:[2,164],72:[2,164],77:[2,164],85:[2,164],90:[2,164],92:[2,164],101:[2,164],102:87,103:[2,164],104:[1,275],105:[2,164],108:88,109:[2,164],110:69,117:[2,164],125:[2,164],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,277],25:[1,278],77:[1,276]},{6:[2,54],11:168,25:[2,54],26:[2,54],27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:279,42:167,44:171,46:[1,46],77:[2,54],88:[1,113]},{8:280,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,281],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,85],6:[2,85],25:[2,85],26:[2,85],40:[2,85],49:[2,85],54:[2,85],57:[2,85],66:[2,85],67:[2,85],68:[2,85],70:[2,85],72:[2,85],73:[2,85],77:[2,85],79:[2,85],83:[2,85],84:[2,85],85:[2,85],90:[2,85],92:[2,85],101:[2,85],103:[2,85],104:[2,85],105:[2,85],109:[2,85],117:[2,85],125:[2,85],127:[2,85],128:[2,85],129:[2,85],130:[2,85],131:[2,85],132:[2,85],133:[2,85],134:[2,85],135:[2,85],136:[2,85],137:[2,85]},{8:282,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,72:[2,120],75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{72:[2,121],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,37],6:[2,37],25:[2,37],26:[2,37],49:[2,37],54:[2,37],57:[2,37],72:[2,37],77:[2,37],85:[2,37],90:[2,37],92:[2,37],101:[2,37],102:87,103:[2,37],104:[2,37],105:[2,37],108:88,109:[2,37],110:69,117:[2,37],125:[2,37],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{26:[1,283],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,266],25:[1,267],85:[1,284]},{6:[2,67],25:[2,67],26:[2,67],54:[2,67],85:[2,67],90:[2,67]},{5:285,25:[1,5]},{6:[2,57],25:[2,57],26:[2,57],49:[2,57],54:[2,57]},{27:109,28:[1,73],44:110,55:286,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{6:[2,55],25:[2,55],26:[2,55],27:109,28:[1,73],44:110,48:287,54:[2,55],55:107,56:108,58:111,59:112,75:[1,70],88:[1,113],89:[1,114]},{6:[2,62],25:[2,62],26:[2,62],49:[2,62],54:[2,62],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{26:[1,288],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{5:289,25:[1,5],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{5:290,25:[1,5]},{1:[2,134],6:[2,134],25:[2,134],26:[2,134],49:[2,134],54:[2,134],57:[2,134],72:[2,134],77:[2,134],85:[2,134],90:[2,134],92:[2,134],101:[2,134],103:[2,134],104:[2,134],105:[2,134],109:[2,134],117:[2,134],125:[2,134],127:[2,134],128:[2,134],131:[2,134],132:[2,134],133:[2,134],134:[2,134],135:[2,134],136:[2,134]},{5:291,25:[1,5]},{26:[1,292],120:[1,293],121:258,122:[1,217]},{1:[2,172],6:[2,172],25:[2,172],26:[2,172],49:[2,172],54:[2,172],57:[2,172],72:[2,172],77:[2,172],85:[2,172],90:[2,172],92:[2,172],101:[2,172],103:[2,172],104:[2,172],105:[2,172],109:[2,172],117:[2,172],125:[2,172],127:[2,172],128:[2,172],131:[2,172],132:[2,172],133:[2,172],134:[2,172],135:[2,172],136:[2,172]},{5:294,25:[1,5]},{26:[2,175],120:[2,175],122:[2,175]},{5:295,25:[1,5],54:[1,296]},{25:[2,130],54:[2,130],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,98],6:[2,98],25:[2,98],26:[2,98],49:[2,98],54:[2,98],57:[2,98],72:[2,98],77:[2,98],85:[2,98],90:[2,98],92:[2,98],101:[2,98],103:[2,98],104:[2,98],105:[2,98],109:[2,98],117:[2,98],125:[2,98],127:[2,98],128:[2,98],131:[2,98],132:[2,98],133:[2,98],134:[2,98],135:[2,98],136:[2,98]},{1:[2,101],5:297,6:[2,101],25:[1,5],26:[2,101],49:[2,101],54:[2,101],57:[2,101],72:[2,101],77:[2,101],85:[2,101],90:[2,101],92:[2,101],101:[2,101],102:87,103:[1,65],104:[2,101],105:[1,66],108:88,109:[1,68],110:69,117:[2,101],125:[2,101],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{101:[1,298]},{90:[1,299],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,115],6:[2,115],25:[2,115],26:[2,115],40:[2,115],49:[2,115],54:[2,115],57:[2,115],66:[2,115],67:[2,115],68:[2,115],70:[2,115],72:[2,115],73:[2,115],77:[2,115],83:[2,115],84:[2,115],85:[2,115],90:[2,115],92:[2,115],101:[2,115],103:[2,115],104:[2,115],105:[2,115],109:[2,115],115:[2,115],116:[2,115],117:[2,115],125:[2,115],127:[2,115],128:[2,115],131:[2,115],132:[2,115],133:[2,115],134:[2,115],135:[2,115],136:[2,115]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],93:300,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:200,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,25:[1,146],27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,60:147,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],86:301,87:[1,58],88:[1,59],89:[1,57],93:145,95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[2,124],25:[2,124],26:[2,124],54:[2,124],85:[2,124],90:[2,124]},{6:[1,266],25:[1,267],26:[1,302]},{1:[2,141],6:[2,141],25:[2,141],26:[2,141],49:[2,141],54:[2,141],57:[2,141],72:[2,141],77:[2,141],85:[2,141],90:[2,141],92:[2,141],101:[2,141],102:87,103:[1,65],104:[2,141],105:[1,66],108:88,109:[1,68],110:69,117:[2,141],125:[2,141],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,143],6:[2,143],25:[2,143],26:[2,143],49:[2,143],54:[2,143],57:[2,143],72:[2,143],77:[2,143],85:[2,143],90:[2,143],92:[2,143],101:[2,143],102:87,103:[1,65],104:[2,143],105:[1,66],108:88,109:[1,68],110:69,117:[2,143],125:[2,143],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{115:[2,162],116:[2,162]},{8:303,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:304,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:305,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,89],6:[2,89],25:[2,89],26:[2,89],40:[2,89],49:[2,89],54:[2,89],57:[2,89],66:[2,89],67:[2,89],68:[2,89],70:[2,89],72:[2,89],73:[2,89],77:[2,89],83:[2,89],84:[2,89],85:[2,89],90:[2,89],92:[2,89],101:[2,89],103:[2,89],104:[2,89],105:[2,89],109:[2,89],115:[2,89],116:[2,89],117:[2,89],125:[2,89],127:[2,89],128:[2,89],131:[2,89],132:[2,89],133:[2,89],134:[2,89],135:[2,89],136:[2,89]},{11:168,27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:306,42:167,44:171,46:[1,46],88:[1,113]},{6:[2,90],11:168,25:[2,90],26:[2,90],27:169,28:[1,73],29:170,30:[1,71],31:[1,72],41:166,42:167,44:171,46:[1,46],54:[2,90],76:307,88:[1,113]},{6:[2,92],25:[2,92],26:[2,92],54:[2,92],77:[2,92]},{6:[2,40],25:[2,40],26:[2,40],54:[2,40],77:[2,40],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{8:308,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{72:[2,119],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,38],6:[2,38],25:[2,38],26:[2,38],49:[2,38],54:[2,38],57:[2,38],72:[2,38],77:[2,38],85:[2,38],90:[2,38],92:[2,38],101:[2,38],103:[2,38],104:[2,38],105:[2,38],109:[2,38],117:[2,38],125:[2,38],127:[2,38],128:[2,38],131:[2,38],132:[2,38],133:[2,38],134:[2,38],135:[2,38],136:[2,38]},{1:[2,110],6:[2,110],25:[2,110],26:[2,110],49:[2,110],54:[2,110],57:[2,110],66:[2,110],67:[2,110],68:[2,110],70:[2,110],72:[2,110],73:[2,110],77:[2,110],83:[2,110],84:[2,110],85:[2,110],90:[2,110],92:[2,110],101:[2,110],103:[2,110],104:[2,110],105:[2,110],109:[2,110],117:[2,110],125:[2,110],127:[2,110],128:[2,110],131:[2,110],132:[2,110],133:[2,110],134:[2,110],135:[2,110],136:[2,110]},{1:[2,49],6:[2,49],25:[2,49],26:[2,49],49:[2,49],54:[2,49],57:[2,49],72:[2,49],77:[2,49],85:[2,49],90:[2,49],92:[2,49],101:[2,49],103:[2,49],104:[2,49],105:[2,49],109:[2,49],117:[2,49],125:[2,49],127:[2,49],128:[2,49],131:[2,49],132:[2,49],133:[2,49],134:[2,49],135:[2,49],136:[2,49]},{6:[2,58],25:[2,58],26:[2,58],49:[2,58],54:[2,58]},{6:[2,53],25:[2,53],26:[2,53],53:309,54:[1,202]},{1:[2,200],6:[2,200],25:[2,200],26:[2,200],49:[2,200],54:[2,200],57:[2,200],72:[2,200],77:[2,200],85:[2,200],90:[2,200],92:[2,200],101:[2,200],103:[2,200],104:[2,200],105:[2,200],109:[2,200],117:[2,200],125:[2,200],127:[2,200],128:[2,200],131:[2,200],132:[2,200],133:[2,200],134:[2,200],135:[2,200],136:[2,200]},{1:[2,179],6:[2,179],25:[2,179],26:[2,179],49:[2,179],54:[2,179],57:[2,179],72:[2,179],77:[2,179],85:[2,179],90:[2,179],92:[2,179],101:[2,179],103:[2,179],104:[2,179],105:[2,179],109:[2,179],117:[2,179],120:[2,179],125:[2,179],127:[2,179],128:[2,179],131:[2,179],132:[2,179],133:[2,179],134:[2,179],135:[2,179],136:[2,179]},{1:[2,135],6:[2,135],25:[2,135],26:[2,135],49:[2,135],54:[2,135],57:[2,135],72:[2,135],77:[2,135],85:[2,135],90:[2,135],92:[2,135],101:[2,135],103:[2,135],104:[2,135],105:[2,135],109:[2,135],117:[2,135],125:[2,135],127:[2,135],128:[2,135],131:[2,135],132:[2,135],133:[2,135],134:[2,135],135:[2,135],136:[2,135]},{1:[2,136],6:[2,136],25:[2,136],26:[2,136],49:[2,136],54:[2,136],57:[2,136],72:[2,136],77:[2,136],85:[2,136],90:[2,136],92:[2,136],97:[2,136],101:[2,136],103:[2,136],104:[2,136],105:[2,136],109:[2,136],117:[2,136],125:[2,136],127:[2,136],128:[2,136],131:[2,136],132:[2,136],133:[2,136],134:[2,136],135:[2,136],136:[2,136]},{1:[2,170],6:[2,170],25:[2,170],26:[2,170],49:[2,170],54:[2,170],57:[2,170],72:[2,170],77:[2,170],85:[2,170],90:[2,170],92:[2,170],101:[2,170],103:[2,170],104:[2,170],105:[2,170],109:[2,170],117:[2,170],125:[2,170],127:[2,170],128:[2,170],131:[2,170],132:[2,170],133:[2,170],134:[2,170],135:[2,170],136:[2,170]},{5:310,25:[1,5]},{26:[1,311]},{6:[1,312],26:[2,176],120:[2,176],122:[2,176]},{8:313,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{1:[2,102],6:[2,102],25:[2,102],26:[2,102],49:[2,102],54:[2,102],57:[2,102],72:[2,102],77:[2,102],85:[2,102],90:[2,102],92:[2,102],101:[2,102],103:[2,102],104:[2,102],105:[2,102],109:[2,102],117:[2,102],125:[2,102],127:[2,102],128:[2,102],131:[2,102],132:[2,102],133:[2,102],134:[2,102],135:[2,102],136:[2,102]},{1:[2,139],6:[2,139],25:[2,139],26:[2,139],49:[2,139],54:[2,139],57:[2,139],66:[2,139],67:[2,139],68:[2,139],70:[2,139],72:[2,139],73:[2,139],77:[2,139],83:[2,139],84:[2,139],85:[2,139],90:[2,139],92:[2,139],101:[2,139],103:[2,139],104:[2,139],105:[2,139],109:[2,139],117:[2,139],125:[2,139],127:[2,139],128:[2,139],131:[2,139],132:[2,139],133:[2,139],134:[2,139],135:[2,139],136:[2,139]},{1:[2,118],6:[2,118],25:[2,118],26:[2,118],49:[2,118],54:[2,118],57:[2,118],66:[2,118],67:[2,118],68:[2,118],70:[2,118],72:[2,118],73:[2,118],77:[2,118],83:[2,118],84:[2,118],85:[2,118],90:[2,118],92:[2,118],101:[2,118],103:[2,118],104:[2,118],105:[2,118],109:[2,118],117:[2,118],125:[2,118],127:[2,118],128:[2,118],131:[2,118],132:[2,118],133:[2,118],134:[2,118],135:[2,118],136:[2,118]},{6:[2,125],25:[2,125],26:[2,125],54:[2,125],85:[2,125],90:[2,125]},{6:[2,53],25:[2,53],26:[2,53],53:314,54:[1,226]},{6:[2,126],25:[2,126],26:[2,126],54:[2,126],85:[2,126],90:[2,126]},{1:[2,165],6:[2,165],25:[2,165],26:[2,165],49:[2,165],54:[2,165],57:[2,165],72:[2,165],77:[2,165],85:[2,165],90:[2,165],92:[2,165],101:[2,165],102:87,103:[2,165],104:[2,165],105:[2,165],108:88,109:[2,165],110:69,117:[1,315],125:[2,165],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,167],6:[2,167],25:[2,167],26:[2,167],49:[2,167],54:[2,167],57:[2,167],72:[2,167],77:[2,167],85:[2,167],90:[2,167],92:[2,167],101:[2,167],102:87,103:[2,167],104:[1,316],105:[2,167],108:88,109:[2,167],110:69,117:[2,167],125:[2,167],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,166],6:[2,166],25:[2,166],26:[2,166],49:[2,166],54:[2,166],57:[2,166],72:[2,166],77:[2,166],85:[2,166],90:[2,166],92:[2,166],101:[2,166],102:87,103:[2,166],104:[2,166],105:[2,166],108:88,109:[2,166],110:69,117:[2,166],125:[2,166],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[2,93],25:[2,93],26:[2,93],54:[2,93],77:[2,93]},{6:[2,53],25:[2,53],26:[2,53],53:317,54:[1,236]},{26:[1,318],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,247],25:[1,248],26:[1,319]},{26:[1,320]},{1:[2,173],6:[2,173],25:[2,173],26:[2,173],49:[2,173],54:[2,173],57:[2,173],72:[2,173],77:[2,173],85:[2,173],90:[2,173],92:[2,173],101:[2,173],103:[2,173],104:[2,173],105:[2,173],109:[2,173],117:[2,173],125:[2,173],127:[2,173],128:[2,173],131:[2,173],132:[2,173],133:[2,173],134:[2,173],135:[2,173],136:[2,173]},{26:[2,177],120:[2,177],122:[2,177]},{25:[2,131],54:[2,131],102:87,103:[1,65],105:[1,66],108:88,109:[1,68],110:69,125:[1,86],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[1,266],25:[1,267],26:[1,321]},{8:322,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{8:323,9:117,10:20,11:21,12:[1,22],13:8,14:9,15:10,16:11,17:12,18:13,19:14,20:15,21:16,22:17,23:18,24:19,27:62,28:[1,73],29:49,30:[1,71],31:[1,72],32:24,33:[1,50],34:[1,51],35:[1,52],36:[1,53],37:[1,54],38:[1,55],39:23,44:63,45:[1,45],46:[1,46],47:[1,29],50:30,51:[1,60],52:[1,61],58:47,59:48,61:36,63:25,64:26,65:27,75:[1,70],78:[1,43],82:[1,28],87:[1,58],88:[1,59],89:[1,57],95:[1,38],99:[1,44],100:[1,56],102:39,103:[1,65],105:[1,66],106:40,107:[1,67],108:41,109:[1,68],110:69,118:[1,42],123:37,124:[1,64],126:[1,31],127:[1,32],128:[1,33],129:[1,34],130:[1,35]},{6:[1,277],25:[1,278],26:[1,324]},{6:[2,41],25:[2,41],26:[2,41],54:[2,41],77:[2,41]},{6:[2,59],25:[2,59],26:[2,59],49:[2,59],54:[2,59]},{1:[2,171],6:[2,171],25:[2,171],26:[2,171],49:[2,171],54:[2,171],57:[2,171],72:[2,171],77:[2,171],85:[2,171],90:[2,171],92:[2,171],101:[2,171],103:[2,171],104:[2,171],105:[2,171],109:[2,171],117:[2,171],125:[2,171],127:[2,171],128:[2,171],131:[2,171],132:[2,171],133:[2,171],134:[2,171],135:[2,171],136:[2,171]},{6:[2,127],25:[2,127],26:[2,127],54:[2,127],85:[2,127],90:[2,127]},{1:[2,168],6:[2,168],25:[2,168],26:[2,168],49:[2,168],54:[2,168],57:[2,168],72:[2,168],77:[2,168],85:[2,168],90:[2,168],92:[2,168],101:[2,168],102:87,103:[2,168],104:[2,168],105:[2,168],108:88,109:[2,168],110:69,117:[2,168],125:[2,168],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{1:[2,169],6:[2,169],25:[2,169],26:[2,169],49:[2,169],54:[2,169],57:[2,169],72:[2,169],77:[2,169],85:[2,169],90:[2,169],92:[2,169],101:[2,169],102:87,103:[2,169],104:[2,169],105:[2,169],108:88,109:[2,169],110:69,117:[2,169],125:[2,169],127:[1,80],128:[1,79],131:[1,78],132:[1,81],133:[1,82],134:[1,83],135:[1,84],136:[1,85]},{6:[2,94],25:[2,94],26:[2,94],54:[2,94],77:[2,94]}],defaultActions:{60:[2,51],61:[2,52],75:[2,3],94:[2,108],189:[2,88]},parseError:function(b,c){throw new Error(b)},parse:function(b){function o(a){d.length=d.length-2*a,e.length=e.length-a,f.length=f.length-a}function p(){var a;return a=c.lexer.lex()||1,typeof a!="number"&&(a=c.symbols_[a]||a),a}var c=this,d=[0],e=[null],f=[],g=this.table,h="",i=0,j=0,k=0,l=2,m=1;this.lexer.setInput(b),this.lexer.yy=this.yy,this.yy.lexer=this.lexer,typeof this.lexer.yylloc=="undefined"&&(this.lexer.yylloc={});var n=this.lexer.yylloc;f.push(n),typeof this.yy.parseError=="function"&&(this.parseError=this.yy.parseError);var q,r,s,t,u,v,w={},x,y,z,A;for(;;){s=d[d.length-1],this.defaultActions[s]?t=this.defaultActions[s]:(q==null&&(q=p()),t=g[s]&&g[s][q]);if(typeof t=="undefined"||!t.length||!t[0]){if(!k){A=[];for(x in g[s])this.terminals_[x]&&x>2&&A.push("'"+this.terminals_[x]+"'");var B="";this.lexer.showPosition?B="Parse error on line "+(i+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+A.join(", ")+", got '"+this.terminals_[q]+"'":B="Parse error on line "+(i+1)+": Unexpected "+(q==1?"end of input":"'"+(this.terminals_[q]||q)+"'"),this.parseError(B,{text:this.lexer.match,token:this.terminals_[q]||q,line:this.lexer.yylineno,loc:n,expected:A})}if(k==3){if(q==m)throw new Error(B||"Parsing halted.");j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,q=p()}for(;;){if(l.toString()in g[s])break;if(s==0)throw new Error(B||"Parsing halted.");o(1),s=d[d.length-1]}r=q,q=l,s=d[d.length-1],t=g[s]&&g[s][l],k=3}if(t[0]instanceof Array&&t.length>1)throw new Error("Parse Error: multiple actions possible at state: "+s+", token: "+q);switch(t[0]){case 1:d.push(q),e.push(this.lexer.yytext),f.push(this.lexer.yylloc),d.push(t[1]),q=null,r?(q=r,r=null):(j=this.lexer.yyleng,h=this.lexer.yytext,i=this.lexer.yylineno,n=this.lexer.yylloc,k>0&&k--);break;case 2:y=this.productions_[t[1]][1],w.$=e[e.length-y],w._$={first_line:f[f.length-(y||1)].first_line,last_line:f[f.length-1].last_line,first_column:f[f.length-(y||1)].first_column,last_column:f[f.length-1].last_column},v=this.performAction.call(w,h,j,i,this.yy,t[1],e,f);if(typeof v!="undefined")return v;y&&(d=d.slice(0,-1*y*2),e=e.slice(0,-1*y),f=f.slice(0,-1*y)),d.push(this.productions_[t[1]][0]),e.push(w.$),f.push(w._$),z=g[d[d.length-2]][d[d.length-1]],d.push(z);break;case 3:return!0}}return!0}};return undefined,a}();typeof require!="undefined"&&typeof a!="undefined"&&(a.parser=b,a.parse=function(){return b.parse.apply(b,arguments)},a.main=function(c){if(!c[1])throw new Error("Usage: "+c[0]+" FILE");if(typeof process!="undefined")var d=require("fs").readFileSync(require("path").join(process.cwd(),c[1]),"utf8");else var e=require("file").path(require("file").cwd()),d=e.join(c[1]).read({charset:"utf-8"});return a.parser.parse(d)},typeof module!="undefined"&&require.main===module&&a.main(typeof process!="undefined"?process.argv.slice(1):require("system").args))},require["./scope"]=new function(){var a=this;((function(){var b,c,d,e;e=require("./helpers"),c=e.extend,d=e.last,a.Scope=b=function(){function a(b,c,d){this.parent=b,this.expressions=c,this.method=d,this.variables=[{name:"arguments",type:"arguments"}],this.positions={},this.parent||(a.root=this)}return a.root=null,a.prototype.add=function(a,b,c){return this.shared&&!c?this.parent.add(a,b,c):Object.prototype.hasOwnProperty.call(this.positions,a)?this.variables[this.positions[a]].type=b:this.positions[a]=this.variables.push({name:a,type:b})-1},a.prototype.namedMethod=function(){return this.method.name||!this.parent?this.method:this.parent.namedMethod()},a.prototype.find=function(a){return this.check(a)?!0:(this.add(a,"var"),!1)},a.prototype.parameter=function(a){if(this.shared&&this.parent.check(a,!0))return;return this.add(a,"param")},a.prototype.check=function(a){var b;return!!(this.type(a)||((b=this.parent)!=null?b.check(a):void 0))},a.prototype.temporary=function(a,b){return a.length>1?"_"+a+(b>1?b-1:""):"_"+(b+parseInt(a,36)).toString(36).replace(/\d/g,"a")},a.prototype.type=function(a){var b,c,d,e;e=this.variables;for(c=0,d=e.length;c1&&a.level>=w?"("+c+")":c)},b.prototype.compileRoot=function(a){var b,c,d,e,f,g;return a.indent=a.bare?"":R,a.scope=new N(null,this,null),a.level=z,this.spaced=!0,e="",a.bare||(f=function(){var a,b,e,f;e=this.expressions,f=[];for(d=a=0,b=e.length;a=u?"(void 0)":"void 0"},b}(e),a.Null=function(a){function b(){return b.__super__.constructor.apply(this,arguments)}return bm(b,a),b.prototype.isAssignable=D,b.prototype.isComplex=D,b.prototype.compileNode=function(){return"null"},b}(e),a.Bool=function(a){function b(a){this.val=a}return bm(b,a),b.prototype.isAssignable=D,b.prototype.isComplex=D,b.prototype.compileNode=function(){return this.val},b}(e),a.Return=K=function(a){function b(a){a&&!a.unwrap().isUndefined&&(this.expression=a)}return bm(b,a),b.prototype.children=["expression"],b.prototype.isStatement=Y,b.prototype.makeReturn=S,b.prototype.jumps=S,b.prototype.compile=function(a,c){var d,e;return d=(e=this.expression)!=null?e.makeReturn():void 0,!d||d instanceof b?b.__super__.compile.call(this,a,c):d.compile(a,c)},b.prototype.compileNode=function(a){return this.tab+("return"+[this.expression?" "+this.expression.compile(a,y):void 0]+";")},b}(e),a.Value=W=function(a){function b(a,c,d){return!c&&a instanceof b?a:(this.base=a,this.properties=c||[],d&&(this[d]=!0),this)}return bm(b,a),b.prototype.children=["base","properties"],b.prototype.add=function(a){return this.properties=this.properties.concat(a),this},b.prototype.hasProperties=function(){return!!this.properties.length},b.prototype.isArray=function(){return!this.properties.length&&this.base instanceof c},b.prototype.isComplex=function(){return this.hasProperties()||this.base.isComplex()},b.prototype.isAssignable=function(){return this.hasProperties()||this.base.isAssignable()},b.prototype.isSimpleNumber=function(){return this.base instanceof A&&L.test(this.base.value)},b.prototype.isString=function(){return this.base instanceof A&&q.test(this.base.value)},b.prototype.isAtomic=function(){var a,b,c,d;d=this.properties.concat(this.base);for(b=0,c=d.length;b"+this.equals],i=n[0],e=n[1],c=this.stepNum?+this.stepNum>0?""+i+" "+this.toVar:""+e+" "+this.toVar:h?(o=[+this.fromNum,+this.toNum],d=o[0],l=o[1],o,d<=l?""+i+" "+l:""+e+" "+l):(b=""+this.fromVar+" <= "+this.toVar,""+b+" ? "+i+" "+this.toVar+" : "+e+" "+this.toVar),k=this.stepVar?""+f+" += "+this.stepVar:h?j?d<=l?"++"+f:"--"+f:d<=l?""+f+"++":""+f+"--":j?""+b+" ? ++"+f+" : --"+f:""+b+" ? "+f+"++ : "+f+"--",j&&(m=""+g+" = "+m),j&&(k=""+g+" = "+k),""+m+"; "+c+"; "+k):this.compileArray(a)},b.prototype.compileArray=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;if(this.fromNum&&this.toNum&&Math.abs(this.fromNum-this.toNum)<=20)return j=function(){p=[];for(var a=n=+this.fromNum,b=+this.toNum;n<=b?a<=b:a>=b;n<=b?a++:a--)p.push(a);return p}.apply(this),this.exclusive&&j.pop(),"["+j.join(", ")+"]";g=this.tab+R,f=a.scope.freeVariable("i"),k=a.scope.freeVariable("results"),i="\n"+g+k+" = [];",this.fromNum&&this.toNum?(a.index=f,c=this.compileNode(a)):(l=""+f+" = "+this.fromC+(this.toC!==this.toVar?", "+this.toC:""),d=""+this.fromVar+" <= "+this.toVar,c="var "+l+"; "+d+" ? "+f+" <"+this.equals+" "+this.toVar+" : "+f+" >"+this.equals+" "+this.toVar+"; "+d+" ? "+f+"++ : "+f+"--"),h="{ "+k+".push("+f+"); }\n"+g+"return "+k+";\n"+a.indent,e=function(a){return a!=null?a.contains(function(a){return a instanceof A&&a.value==="arguments"&&!a.asKey}):void 0};if(e(this.from)||e(this.to))b=", arguments";return"(function() {"+i+"\n"+g+"for ("+c+")"+h+"}).apply(this"+(b!=null?b:"")+")"},b}(e),a.Slice=O=function(a){function b(a){this.range=a,b.__super__.constructor.call(this)}return bm(b,a),b.prototype.children=["range"],b.prototype.compileNode=function(a){var b,c,d,e,f,g;return g=this.range,e=g.to,c=g.from,d=c&&c.compile(a,y)||"0",b=e&&e.compile(a,y),e&&(!!this.range.exclusive||+b!==-1)&&(f=", "+(this.range.exclusive?b:L.test(b)?""+(+b+1):(b=e.compile(a,u),"+"+b+" + 1 || 9e9"))),".slice("+d+(f||"")+")"},b}(e),a.Obj=E=function(a){function b(a,b){this.generated=b!=null?b:!1,this.objects=this.properties=a||[]}return bm(b,a),b.prototype.children=["properties"],b.prototype.compileNode=function(a){var b,c,e,f,g,h,i,j,l,m,n;l=this.properties;if(!l.length)return this.front?"({})":"{}";if(this.generated)for(m=0,n=l.length;m=0?"[\n"+a.indent+b+"\n"+this.tab+"]":"["+b+"]")):"[]"},b.prototype.assigns=function(a){var b,c,d,e;e=this.objects;for(c=0,d=e.length;c=0)throw SyntaxError("variable name may not be "+a);return a&&(a=o.test(a)&&a)},c.prototype.setContext=function(a){return this.body.traverseChildren(!1,function(b){if(b.classBody)return!1;if(b instanceof A&&b.value==="this")return b.value=a;if(b instanceof j){b.klass=a;if(b.bound)return b.context=a}})},c.prototype.addBoundFunctions=function(a){var c,d,e,f,g,h;if(this.boundFuncs.length){g=this.boundFuncs,h=[];for(e=0,f=g.length;e=0);if(e&&this.context!=="object")throw SyntaxError('variable name may not be "'+f+'"')}return bm(c,a),c.prototype.children=["variable","value"],c.prototype.isStatement=function(a){return(a!=null?a.level:void 0)===z&&this.context!=null&&bn.call(this.context,"?")>=0},c.prototype.assigns=function(a){return this[this.context==="object"?"value":"variable"].assigns(a)},c.prototype.unfoldSoak=function(a){return bh(a,this,"variable")},c.prototype.compileNode=function(a){var b,c,d,e,f,g,h,i,k;if(b=this.variable instanceof W){if(this.variable.isArray()||this.variable.isObject())return this.compilePatternMatch(a);if(this.variable.isSplice())return this.compileSplice(a);if((g=this.context)==="||="||g==="&&="||g==="?=")return this.compileConditional(a)}d=this.variable.compile(a,w);if(!this.context){if(!(f=this.variable.unwrapAll()).isAssignable())throw SyntaxError('"'+this.variable.compile(a)+'" cannot be assigned.');if(typeof f.hasProperties=="function"?!f.hasProperties():!void 0)this.param?a.scope.add(d,"var"):a.scope.find(d)}return this.value instanceof j&&(c=B.exec(d))&&(c[1]&&(this.value.klass=c[1]),this.value.name=(h=(i=(k=c[2])!=null?k:c[3])!=null?i:c[4])!=null?h:c[5]),e=this.value.compile(a,w),this.context==="object"?""+d+": "+e:(e=d+(" "+(this.context||"=")+" ")+e,a.level<=w?e:"("+e+")")},c.prototype.compilePatternMatch=function(a){var d,e,f,g,h,i,j,k,l,m,n,p,q,r,s,u,v,y,B,C,D,E,F,G,J,K,L;s=a.level===z,v=this.value,m=this.variable.base.objects;if(!(n=m.length))return f=v.compile(a),a.level>=x?"("+f+")":f;i=this.variable.isObject();if(s&&n===1&&!((l=m[0])instanceof P)){l instanceof c?(D=l,E=D.variable,h=E.base,l=D.value):l.base instanceof H?(F=(new W(l.unwrapAll())).cacheReference(a),l=F[0],h=F[1]):h=i?l["this"]?l.properties[0].name:l:new A(0),d=o.test(h.unwrap().value||0),v=new W(v),v.properties.push(new(d?b:t)(h));if(G=l.unwrap().value,bn.call(I,G)>=0)throw new SyntaxError("assignment to a reserved word: "+l.compile(a)+" = "+v.compile(a));return(new c(l,v,null,{param:this.param})).compile(a,z)}y=v.compile(a,w),e=[],r=!1;if(!o.test(y)||this.variable.assigns(y))e.push(""+(p=a.scope.freeVariable("ref"))+" = "+y),y=p;for(g=B=0,C=m.length;B=0)throw new SyntaxError("assignment to a reserved word: "+l.compile(a)+" = "+u.compile(a));e.push((new c(l,u,null,{param:this.param,subpattern:!0})).compile(a,w))}return!s&&!this.subpattern&&e.push(y),f=e.join(", "),a.level=0&&(a.isExistentialEquals=!0),(new F(this.context.slice(0,-1),b,new c(d,this.value,"="))).compile(a)},c.prototype.compileSplice=function(a){var b,c,d,e,f,g,h,i,j,k,l,m;return k=this.variable.properties.pop().range,d=k.from,h=k.to,c=k.exclusive,g=this.variable.compile(a),l=(d!=null?d.cache(a,x):void 0)||["0","0"],e=l[0],f=l[1],h?(d!=null?d.isSimpleNumber():void 0)&&h.isSimpleNumber()?(h=+h.compile(a)- +f,c||(h+=1)):(h=h.compile(a,u)+" - "+f,c||(h+=" + 1")):h="9e9",m=this.value.cache(a,w),i=m[0],j=m[1],b="[].splice.apply("+g+", ["+e+", "+h+"].concat("+i+")), "+j,a.level>z?"("+b+")":b},c}(e),a.Code=j=function(a){function b(a,b,c){this.params=a||[],this.body=b||new f,this.bound=c==="boundfunc",this.bound&&(this.context="_this")}return bm(b,a),b.prototype.children=["params","body"],b.prototype.isStatement=function(){return!!this.ctor},b.prototype.jumps=D,b.prototype.compileNode=function(a){var b,e,f,g,h,i,j,k,l,m,n,o,p,q,s,t,v,w,x,y,z,B,C,D,E,G,H,I,J,K,L,M,O;a.scope=new N(a.scope,this.body,this),a.scope.shared=$(a,"sharedScope"),a.indent+=R,delete a.bare,delete a.isExistentialEquals,l=[],e=[],H=this.paramNames();for(s=0,x=H.length;s=0)throw SyntaxError("multiple parameters named '"+i+"'");o.push(i)}return!q&&!this.noReturn&&this.body.makeReturn(),this.bound&&(((O=a.scope.parent.method)!=null?O.bound:void 0)?this.bound=this.context=a.scope.parent.method.context:this["static"]||a.scope.parent.assign("_this","this")),g=a.indent,b="function",this.ctor&&(b+=" "+this.name),b+="("+l.join(", ")+") {",this.body.isEmpty()||(b+="\n"+this.body.compileWithDeclarations(a)+"\n"+this.tab),b+="}",this.ctor?this.tab+b:this.front||a.level>=u?"("+b+")":b},b.prototype.paramNames=function(){var a,b,c,d,e;a=[],e=this.params;for(c=0,d=e.length;c=0)throw SyntaxError('parameter name "'+a+'" is not allowed')}return bm(b,a),b.prototype.children=["name","value"],b.prototype.compile=function(a){return this.name.compile(a,w)},b.prototype.asReference=function(a){var b;return this.reference?this.reference:(b=this.name,b["this"]?(b=b.properties[0].name,b.value.reserved&&(b=new A(a.scope.freeVariable(b.value)))):b.isComplex()&&(b=new A(a.scope.freeVariable("arg"))),b=new W(b),this.splat&&(b=new P(b)),this.reference=b)},b.prototype.isComplex=function(){return this.name.isComplex()},b.prototype.names=function(a){var b,c,e,f,g,h;a==null&&(a=this.name),b=function(a){var b;return b=a.properties[0].name.value,b.reserved?[]:[b]};if(a instanceof A)return[a.value];if(a instanceof W)return b(a);c=[],h=a.objects;for(f=0,g=h.length;f=c.length)return"";if(c.length===1)return g=c[0].compile(a,w),d?g:""+bi("slice")+".call("+g+")";e=c.slice(i);for(h=k=0,l=e.length;k1?b.expressions.unshift(new r((new H(this.guard)).invert(),new A("continue"))):this.guard&&(b=f.wrap([new r(this.guard,b)]))),b="\n"+b.compile(a,z)+"\n"+this.tab),c=e+this.tab+("while ("+this.condition.compile(a,y)+") {"+b+"}"),this.returns&&(c+="\n"+this.tab+"return "+d+";"),c},b}(e),a.Op=F=function(a){function e(a,c,d,e){if(a==="in")return new s(c,d);if(a==="do")return this.generateDo(c);if(a==="new"){if(c instanceof g&&!c["do"]&&!c.isNew)return c.newInstance();if(c instanceof j&&c.bound||c["do"])c=new H(c)}return this.operator=b[a]||a,this.first=c,this.second=d,this.flip=!!e,this}var b,c;return bm(e,a),b={"==":"===","!=":"!==",of:"in"},c={"!==":"===","===":"!=="},e.prototype.children=["first","second"],e.prototype.isSimpleNumber=D,e.prototype.isUnary=function(){return!this.second},e.prototype.isComplex=function(){var a;return!this.isUnary()||(a=this.operator)!=="+"&&a!=="-"||this.first.isComplex()},e.prototype.isChainable=function(){var a;return(a=this.operator)==="<"||a===">"||a===">="||a==="<="||a==="==="||a==="!=="},e.prototype.invert=function(){var a,b,d,f,g;if(this.isChainable()&&this.first.isChainable()){a=!0,b=this;while(b&&b.operator)a&&(a=b.operator in c),b=b.first;if(!a)return(new H(this)).invert();b=this;while(b&&b.operator)b.invert=!b.invert,b.operator=c[b.operator],b=b.first;return this}return(f=c[this.operator])?(this.operator=f,this.first.unwrap()instanceof e&&this.first.invert(),this):this.second?(new H(this)).invert():this.operator==="!"&&(d=this.first.unwrap())instanceof e&&((g=d.operator)==="!"||g==="in"||g==="instanceof")?d:new e("!",this)},e.prototype.unfoldSoak=function(a){var b;return((b=this.operator)==="++"||b==="--"||b==="delete")&&bh(a,this,"first")},e.prototype.generateDo=function(a){var b,c,e,f,h,i,k,l;f=[],c=a instanceof d&&(h=a.value.unwrap())instanceof j?h:a,l=c.params||[];for(i=0,k=l.length;i=0))throw SyntaxError("prefix increment/decrement may not have eval or arguments operand");return this.isUnary()?this.compileUnary(a):c?this.compileChain(a):this.operator==="?"?this.compileExistence(a):(b=this.first.compile(a,x)+" "+this.operator+" "+this.second.compile(a,x),a.level<=x?b:"("+b+")")},e.prototype.compileChain=function(a){var b,c,d,e;return e=this.first.second.cache(a),this.first.second=e[0],d=e[1],c=this.first.compile(a,x),b=""+c+" "+(this.invert?"&&":"||")+" "+d.compile(a)+" "+this.operator+" "+this.second.compile(a,x),"("+b+")"},e.prototype.compileExistence=function(a){var b,c;return this.first.isComplex()?(c=new A(a.scope.freeVariable("ref")),b=new H(new d(c,this.first))):(b=this.first,c=b),(new r(new l(b),c,{type:"if"})).addElse(this.second).compile(a)},e.prototype.compileUnary=function(a){var b,c,d;if(a.level>=u)return(new H(this)).compile(a);c=[b=this.operator],d=b==="+"||b==="-",(b==="new"||b==="typeof"||b==="delete"||d&&this.first instanceof e&&this.first.operator===b)&&c.push(" ");if(d&&this.first instanceof e||b==="new"&&this.first.isStatement(a))this.first=new H(this.first);return c.push(this.first.compile(a,x)),this.flip&&c.reverse(),c.join("")},e.prototype.toString=function(a){return e.__super__.toString.call(this,a,this.constructor.name+" "+this.operator)},e}(e),a.In=s=function(a){function b(a,b){this.object=a,this.array=b}return bm(b,a),b.prototype.children=["object","array"],b.prototype.invert=C,b.prototype.compileNode=function(a){var b,c,d,e,f;if(this.array instanceof W&&this.array.isArray()){f=this.array.base.objects;for(d=0,e=f.length;d= 0"),d===c?b:(b=d+", "+b,a.level=0)throw SyntaxError('catch variable may not be "'+this.error.value+'"');return a.scope.check(this.error.value)||a.scope.add(this.error.value,"param")," catch"+d+"{\n"+this.recovery.compile(a,z)+"\n"+this.tab+"}"}if(!this.ensure&&!this.recovery)return" catch (_error) {}"}.call(this),c=this.ensure?" finally {\n"+this.ensure.compile(a,z)+"\n"+this.tab+"}":"",""+this.tab+"try {\n"+e+"\n"+this.tab+"}"+(b||"")+c},b}(e),a.Throw=T=function(a){function b(a){this.expression=a}return bm(b,a),b.prototype.children=["expression"],b.prototype.isStatement=Y,b.prototype.jumps=D,b.prototype.makeReturn=S,b.prototype.compileNode=function(a){return this.tab+("throw "+this.expression.compile(a)+";")},b}(e),a.Existence=l=function(a){function b(a){this.expression=a}return bm(b,a),b.prototype.children=["expression"],b.prototype.invert=C,b.prototype.compileNode=function(a){var b,c,d,e;return this.expression.front=this.front,d=this.expression.compile(a,x),o.test(d)&&!a.scope.check(d)?(e=this.negated?["===","||"]:["!==","&&"],b=e[0],c=e[1],d="typeof "+d+" "+b+' "undefined" '+c+" "+d+" "+b+" null"):d=""+d+" "+(this.negated?"==":"!=")+" null",a.level<=v?d:"("+d+")"},b}(e),a.Parens=H=function(a){function b(a){this.body=a}return bm(b,a),b.prototype.children=["body"],b.prototype.unwrap=function(){return this.body},b.prototype.isComplex=function(){return this.body.isComplex()},b.prototype.compileNode=function(a){var b,c,d;return d=this.body.unwrap(),d instanceof W&&d.isAtomic()?(d.front=this.front,d.compile(a)):(c=d.compile(a,y),b=a.level1?b.expressions.unshift(new r((new H(this.guard)).invert(),new A("continue"))):this.guard&&(b=f.wrap([new r(this.guard,b)]))),this.pattern&&b.expressions.unshift(new d(this.name,new A(""+F+"["+l+"]"))),c+=this.pluckDirectCall(a,b),s&&(G="\n"+i+s+";"),this.object&&(e=""+l+" in "+F,this.own&&(h="\n"+i+"if (!"+bi("hasProp")+".call("+F+", "+l+")) continue;")),b=b.compile(bd(a,{indent:i}),z),b&&(b="\n"+b+"\n"),""+c+(u||"")+this.tab+"for ("+e+") {"+h+G+b+this.tab+"}"+(v||"")},b.prototype.pluckDirectCall=function(a,b){var c,e,f,h,i,k,l,m,n,o,p,q,r,s,t;e="",o=b.expressions;for(i=m=0,n=o.length;m=v?"("+d+")":d},b.prototype.unfoldSoak=function(){return this.soak&&this},b}(e),i={wrap:function(a,c,d){var e,h,i,k,l;if(a.jumps())return a;i=new j([],f.wrap([a])),e=[];if((k=a.contains(this.literalArgs))||a.contains(this.literalThis))l=new A(k?"apply":"call"),e=[new A("this")],k&&e.push(new A("arguments")),i=new W(i,[new b(l)]);return i.noReturn=d,h=new g(i,e),c?f.wrap([h]):h},literalArgs:function(a){return a instanceof A&&a.value==="arguments"&&!a.asKey},literalThis:function(a){return a instanceof A&&a.value==="this"&&!a.asKey||a instanceof j&&a.bound||a instanceof g&&a.isSuper}},bh=function(a,b,c){var d;if(!(d=b[c].unfoldSoak(a)))return;return b[c]=d.body,d.body=new W(b),d},V={"extends":function(){return"function(child, parent) { for (var key in parent) { if ("+bi("hasProp")+".call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }"},bind:function(){return"function(fn, me){ return function(){ return fn.apply(me, arguments); }; }"},indexOf:function(){return"[].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }"},hasProp:function(){return"{}.hasOwnProperty"},slice:function(){return"[].slice"}},z=1,y=2,w=3,v=4,x=5,u=6,R=" ",p="[$A-Za-z_\\x7f-\\uffff][$\\w\\x7f-\\uffff]*",o=RegExp("^"+p+"$"),L=/^[+-]?\d+$/,B=RegExp("^(?:("+p+")\\.prototype(?:\\.("+p+")|\\[(\"(?:[^\\\\\"\\r\\n]|\\\\.)*\"|'(?:[^\\\\'\\r\\n]|\\\\.)*')\\]|\\[(0x[\\da-fA-F]+|\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\]))|("+p+")$"),q=/^['"]/,bi=function(a){var b;return b="__"+a,N.root.assign(b,V[a]()),b},be=function(a,b){return a=a.replace(/\n/g,"$&"+b),a.replace(/\s+$/,"")}})).call(this)},require["./coffee-script"]=new function(){var a=this;((function(){var b,c,d,e,f,g,h,i,j,k,l={}.hasOwnProperty;e=require("fs"),h=require("path"),k=require("./lexer"),b=k.Lexer,c=k.RESERVED,g=require("./parser").parser,j=require("vm"),i=function(a){return a.charCodeAt(0)===65279?a.substring(1):a},require.extensions&&(require.extensions[".coffee"]=function(a,b){var c;return c=d(i(e.readFileSync(b,"utf8")),{filename:b}),a._compile(c,b)}),a.VERSION="1.4.0",a.RESERVED=c,a.helpers=require("./helpers"),a.compile=d=function(b,c){var d,e,h;c==null&&(c={}),h=a.helpers.merge;try{e=g.parse(f.tokenize(b)).compile(c);if(!c.header)return e}catch(i){throw c.filename&&(i.message="In "+c.filename+", "+i.message),i}return d="Generated by CoffeeScript "+this.VERSION,"// "+d+"\n"+e},a.tokens=function(a,b){return f.tokenize(a,b)},a.nodes=function(a,b){return typeof a=="string"?g.parse(f.tokenize(a,b)):g.parse(a)},a.run=function(a,b){var c;return b==null&&(b={}),c=require.main,c.filename=process.argv[1]=b.filename?e.realpathSync(b.filename):".",c.moduleCache&&(c.moduleCache={}),c.paths=require("module")._nodeModulePaths(h.dirname(e.realpathSync(b.filename))),h.extname(c.filename)!==".coffee"||require.extensions?c._compile(d(a,b),c.filename):c._compile(a,c.filename)},a.eval=function(a,b){var c,e,f,g,i,k,m,n,o,p,q,r,s,t;b==null&&(b={});if(!(a=a.trim()))return;e=j.Script;if(e){if(b.sandbox!=null){if(b.sandbox instanceof e.createContext().constructor)m=b.sandbox;else{m=e.createContext(),r=b.sandbox;for(g in r){if(!l.call(r,g))continue;n=r[g],m[g]=n}}m.global=m.root=m.GLOBAL=m}else m=global;m.__filename=b.filename||"eval",m.__dirname=h.dirname(m.__filename);if(m===global&&!m.module&&!m.require){c=require("module"),m.module=q=new c(b.modulename||"eval"),m.require=t=function(a){return c._load(a,q,!0)},q.filename=m.__filename,s=Object.getOwnPropertyNames(require);for(o=0,p=s.length;oe;e++)if(e in this&&this[e]===t)return e;return-1};if(t=function(){function t(t){this.client=new e(t)}return t}(),t.ApiError=function(){function t(t,e,r){var n;if(this.method=e,this.url=r,this.status=t.status,n=t.responseType?t.response||t.responseText:t.responseText)try{this.responseText=""+n,this.response=JSON.parse(n)}catch(o){this.response=null}else this.responseText="(no response)",this.response=null}return t.prototype.status=void 0,t.prototype.method=void 0,t.prototype.url=void 0,t.prototype.responseText=void 0,t.prototype.response=void 0,t.prototype.toString=function(){return"Dropbox API error "+this.status+" from "+this.method+" "+this.url+" :: "+this.responseText},t.prototype.inspect=function(){return""+this},t}(),"undefined"!=typeof window&&null!==window?window.atob&&window.btoa?(u=function(t){return window.atob(t)},f=function(t){return window.btoa(t)}):(p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",y=function(t,e,r){var n,o;for(o=3-e,t<<=8*o,n=3;n>=o;)r.push(p.charAt(63&t>>6*n)),n-=1;for(n=e;3>n;)r.push("="),n+=1;return null},l=function(t,e,r){var n,o;for(o=4-e,t<<=6*o,n=2;n>=o;)r.push(String.fromCharCode(255&t>>8*n)),n-=1;return null},f=function(t){var e,r,n,o,i,s;for(o=[],e=0,r=0,n=i=0,s=t.length;s>=0?s>i:i>s;n=s>=0?++i:--i)e=e<<8|t.charCodeAt(n),r+=1,3===r&&(y(e,r,o),e=r=0);return r>0&&y(e,r,o),o.join("")},u=function(t){var e,r,n,o,i,s,a;for(i=[],e=0,n=0,o=s=0,a=t.length;(a>=0?a>s:s>a)&&(r=t.charAt(o),"="!==r);o=a>=0?++s:--s)e=e<<6|p.indexOf(r),n+=1,4===n&&(l(e,n,i),e=n=0);return n>0&&l(e,n,i),i.join("")}):(u=function(t){var e,r;return e=new Buffer(t,"base64"),function(){var t,n,o;for(o=[],r=t=0,n=e.length;n>=0?n>t:t>n;r=n>=0?++t:--t)o.push(String.fromCharCode(e[r]));return o}().join("")},f=function(t){var e,r;return e=new Buffer(function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}()),e.toString("base64")}),t.Client=function(){function r(e){this.sandbox=e.sandbox||!1,this.apiServer=e.server||this.defaultApiServer(),this.authServer=e.authServer||this.defaultAuthServer(),this.fileServer=e.fileServer||this.defaultFileServer(),this.downloadServer=e.downloadServer||this.defaultDownloadServer(),this.onXhr=new t.EventSource({cancelable:!0}),this.onError=new t.EventSource,this.onAuthStateChange=new t.EventSource,this.oauth=new t.Oauth(e),this.driver=null,this.filter=null,this.uid=null,this.authState=null,this.authError=null,this._credentials=null,this.setCredentials(e),this.setupUrls()}return r.prototype.authDriver=function(t){return this.driver=t,this},r.prototype.onXhr=null,r.prototype.onError=null,r.prototype.onAuthStateChange=null,r.prototype.dropboxUid=function(){return this.uid},r.prototype.credentials=function(){return this._credentials||this.computeCredentials(),this._credentials},r.prototype.authenticate=function(r){var n,o,i=this;if(n=null,!this.driver&&this.authState!==e.DONE)throw Error("Call authDriver to set an authentication driver");return o=function(){var s;if(n!==i.authState&&(null!==n&&i.onAuthStateChange.dispatch(i),n=i.authState,i.driver&&i.driver.onAuthStateChange))return i.driver.onAuthStateChange(i,o);switch(i.authState){case e.RESET:return i.requestToken(function(t,r){var n,s;return t?(i.authError=t,i.authState=e.ERROR):(n=r.oauth_token,s=r.oauth_token_secret,i.oauth.setToken(n,s),i.authState=e.REQUEST),i._credentials=null,o()});case e.REQUEST:return s=i.authorizeUrl(i.oauth.token),i.driver.doAuthorize(s,i.oauth.token,i.oauth.tokenSecret,function(){return i.authState=e.AUTHORIZED,i._credentials=null,o()});case e.AUTHORIZED:return i.getAccessToken(function(t,r){return t?(i.authError=t,i.authState=e.ERROR):(i.oauth.setToken(r.oauth_token,r.oauth_token_secret),i.uid=r.uid,i.authState=e.DONE),i._credentials=null,o()});case e.DONE:return r(null,i);case t.SIGNED_OFF:return i.authState=e.RESET,i.reset(),o();case e.ERROR:return r(i.authError)}},o(),this},r.prototype.isAuthenticated=function(){return this.authState===e.DONE},r.prototype.signOut=function(r){var n,o=this;return n=new t.Xhr("POST",this.urls.signOut),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(t){return t?r(t):(o.authState=e.RESET,o.reset(),o.authState=e.SIGNED_OFF,o.onAuthStateChange.dispatch(o),o.driver.onAuthStateChange?o.driver.onAuthStateChange(o,function(){return r(t)}):r(t))})},r.prototype.signOff=function(t){return this.signOut(t)},r.prototype.getUserInfo=function(e){var r;return r=new t.Xhr("GET",this.urls.accountInfo),r.signWithOauth(this.oauth),this.dispatchXhr(r,function(r,n){return e(r,t.UserInfo.parse(n),n)})},r.prototype.readFile=function(e,r,n){var o,i,s,a,h,u;return n||"function"!=typeof r||(n=r,r=null),o={},h=null,s=null,r&&(r.versionTag?o.rev=r.versionTag:r.rev&&(o.rev=r.rev),r.arrayBuffer?h="arraybuffer":r.blob?h="blob":r.binary&&(h="b"),r.length?(null!=r.start?(a=r.start,i=r.start+r.length-1):(a="",i=r.length),s="bytes="+a+"-"+i):null!=r.start&&(s="bytes="+r.start+"-")),u=new t.Xhr("GET",""+this.urls.getFile+"/"+this.urlEncodePath(e)),u.setParams(o).signWithOauth(this.oauth).setResponseType(h),s&&u.setHeader("Range",s),this.dispatchXhr(u,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.writeFile=function(e,r,n,o){var i;return o||"function"!=typeof n||(o=n,n=null),i=t.Xhr.canSendForms&&"object"==typeof r,i?this.writeFileUsingForm(e,r,n,o):this.writeFileUsingPut(e,r,n,o)},r.prototype.writeFileUsingForm=function(e,r,n,o){var i,s,a,h;return a=e.lastIndexOf("/"),-1===a?(i=e,e=""):(i=e.substring(a),e=e.substring(0,a)),s={file:i},n&&(n.noOverwrite&&(s.overwrite="false"),n.lastVersionTag?s.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(s.parent_rev=n.parentRev||n.parent_rev)),h=new t.Xhr("POST",""+this.urls.postFile+"/"+this.urlEncodePath(e)),h.setParams(s).signWithOauth(this.oauth).setFileField("file",i,r,"application/octet-stream"),delete s.file,this.dispatchXhr(h,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.writeFileUsingPut=function(e,r,n,o){var i,s;return i={},n&&(n.noOverwrite&&(i.overwrite="false"),n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev)),s=new t.Xhr("POST",""+this.urls.putFile+"/"+this.urlEncodePath(e)),s.setBody(r).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.resumableUploadStep=function(e,r,n){var o,i;return r?(o={offset:r.offset},r.tag&&(o.upload_id=r.tag)):o={offset:0},i=new t.Xhr("POST",this.urls.chunkedUpload),i.setBody(e).setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return e&&400===e.status&&e.response.upload_id&&e.response.offset?n(null,t.UploadCursor.parse(e.response)):n(e,t.UploadCursor.parse(r))})},r.prototype.resumableUploadFinish=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={upload_id:r.tag},n&&(n.lastVersionTag?i.parent_rev=n.lastVersionTag:(n.parentRev||n.parent_rev)&&(i.parent_rev=n.parentRev||n.parent_rev),n.noOverwrite&&(i.autorename=!0)),s=new t.Xhr("POST",""+this.urls.commitChunkedUpload+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return o(e,t.Stat.parse(r))})},r.prototype.stat=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&(null!=r.version&&(o.rev=r.version),(r.removed||r.deleted)&&(o.include_deleted="true"),r.readDir&&(o.list="true",r.readDir!==!0&&(o.file_limit=""+r.readDir)),r.cacheHash&&(o.hash=r.cacheHash)),o.include_deleted||(o.include_deleted="false"),o.list||(o.list="false"),i=new t.Xhr("GET",""+this.urls.metadata+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i,s;return s=t.Stat.parse(r),o=(null!=r?r.contents:void 0)?function(){var e,n,o,s;for(o=r.contents,s=[],e=0,n=o.length;n>e;e++)i=o[e],s.push(t.Stat.parse(i));return s}():void 0,n(e,s,o)})},r.prototype.readdir=function(t,e,r){var n;return r||"function"!=typeof e||(r=e,e=null),n={readDir:!0},e&&(null!=e.limit&&(n.readDir=e.limit),e.versionTag&&(n.versionTag=e.versionTag)),this.stat(t,n,function(t,e,n){var o,i;return o=n?function(){var t,e,r;for(r=[],t=0,e=n.length;e>t;t++)i=n[t],r.push(i.name);return r}():null,r(t,o,e,n)})},r.prototype.metadata=function(t,e,r){return this.stat(t,e,r)},r.prototype.makeUrl=function(e,r,n){var o,i,s,a,h;return n||"function"!=typeof r||(n=r,r=null),i=r&&(r["long"]||r.longUrl||r.downloadHack)?{short_url:"false"}:{},e=this.urlEncodePath(e),s=""+this.urls.shares+"/"+e,o=!1,a=!1,r&&(r.downloadHack?(o=!0,a=!0):r.download&&(o=!0,s=""+this.urls.media+"/"+e)),h=new t.Xhr("POST",s).setParams(i).signWithOauth(this.oauth),this.dispatchXhr(h,function(e,r){return a&&r&&r.url&&(r.url=r.url.replace(this.authServer,this.downloadServer)),n(e,t.PublicUrl.parse(r,o))})},r.prototype.history=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o={},r&&null!=r.limit&&(o.rev_limit=r.limit),i=new t.Xhr("GET",""+this.urls.revisions+"/"+this.urlEncodePath(e)),i.setParams(o).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){var o,i;return i=r?function(){var e,n,i;for(i=[],e=0,n=r.length;n>e;e++)o=r[e],i.push(t.Stat.parse(o));return i}():void 0,n(e,i)})},r.prototype.revisions=function(t,e,r){return this.history(t,e,r)},r.prototype.thumbnailUrl=function(t,e){var r;return r=this.thumbnailXhr(t,e),r.paramsToUrl().url},r.prototype.readThumbnail=function(e,r,n){var o,i;return n||"function"!=typeof r||(n=r,r=null),o="b",r&&r.blob&&(o="blob"),i=this.thumbnailXhr(e,r),i.setResponseType(o),this.dispatchXhr(i,function(e,r,o){return n(e,r,t.Stat.parse(o))})},r.prototype.thumbnailXhr=function(e,r){var n,o;return n={},r&&(r.format?n.format=r.format:r.png&&(n.format="png"),r.size&&(n.size=r.size)),o=new t.Xhr("GET",""+this.urls.thumbnails+"/"+this.urlEncodePath(e)),o.setParams(n).signWithOauth(this.oauth)},r.prototype.revertFile=function(e,r,n){var o;return o=new t.Xhr("POST",""+this.urls.restore+"/"+this.urlEncodePath(e)),o.setParams({rev:r}).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.restore=function(t,e,r){return this.revertFile(t,e,r)},r.prototype.findByName=function(e,r,n,o){var i,s;return o||"function"!=typeof n||(o=n,n=null),i={query:r},n&&(null!=n.limit&&(i.file_limit=n.limit),(n.removed||n.deleted)&&(i.include_deleted=!0)),s=new t.Xhr("GET",""+this.urls.search+"/"+this.urlEncodePath(e)),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){var n,i;return i=r?function(){var e,o,i;for(i=[],e=0,o=r.length;o>e;e++)n=r[e],i.push(t.Stat.parse(n));return i}():void 0,o(e,i)})},r.prototype.search=function(t,e,r,n){return this.findByName(t,e,r,n)},r.prototype.makeCopyReference=function(e,r){var n;return n=new t.Xhr("GET",""+this.urls.copyRef+"/"+this.urlEncodePath(e)),n.signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.CopyReference.parse(n))})},r.prototype.copyRef=function(t,e){return this.makeCopyReference(t,e)},r.prototype.pullChanges=function(e,r){var n,o;return r||"function"!=typeof e||(r=e,e=null),n=e?e.cursorTag?{cursor:e.cursorTag}:{cursor:e}:{},o=new t.Xhr("POST",this.urls.delta),o.setParams(n).signWithOauth(this.oauth),this.dispatchXhr(o,function(e,n){return r(e,t.PulledChanges.parse(n))})},r.prototype.delta=function(t,e){return this.pullChanges(t,e)},r.prototype.mkdir=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsCreateFolder),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.remove=function(e,r){var n;return n=new t.Xhr("POST",this.urls.fileopsDelete),n.setParams({root:this.fileRoot,path:this.normalizePath(e)}).signWithOauth(this.oauth),this.dispatchXhr(n,function(e,n){return r(e,t.Stat.parse(n))})},r.prototype.unlink=function(t,e){return this.remove(t,e)},r.prototype["delete"]=function(t,e){return this.remove(t,e)},r.prototype.copy=function(e,r,n){var o,i,s;return n||"function"!=typeof o||(n=o,o=null),i={root:this.fileRoot,to_path:this.normalizePath(r)},e instanceof t.CopyReference?i.from_copy_ref=e.tag:i.from_path=this.normalizePath(e),s=new t.Xhr("POST",this.urls.fileopsCopy),s.setParams(i).signWithOauth(this.oauth),this.dispatchXhr(s,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.move=function(e,r,n){var o,i;return n||"function"!=typeof o||(n=o,o=null),i=new t.Xhr("POST",this.urls.fileopsMove),i.setParams({root:this.fileRoot,from_path:this.normalizePath(e),to_path:this.normalizePath(r)}).signWithOauth(this.oauth),this.dispatchXhr(i,function(e,r){return n(e,t.Stat.parse(r))})},r.prototype.reset=function(){var t;return this.uid=null,this.oauth.setToken(null,""),t=this.authState,this.authState=e.RESET,t!==this.authState&&this.onAuthStateChange.dispatch(this),this.authError=null,this._credentials=null,this},r.prototype.setCredentials=function(t){var r;return r=this.authState,this.oauth.reset(t),this.uid=t.uid||null,this.authState=t.authState?t.authState:t.token?e.DONE:e.RESET,this.authError=null,this._credentials=null,r!==this.authState&&this.onAuthStateChange.dispatch(this),this},r.prototype.appHash=function(){return this.oauth.appHash()},r.prototype.setupUrls=function(){return this.fileRoot=this.sandbox?"sandbox":"dropbox",this.urls={requestToken:""+this.apiServer+"/1/oauth/request_token",authorize:""+this.authServer+"/1/oauth/authorize",accessToken:""+this.apiServer+"/1/oauth/access_token",signOut:""+this.apiServer+"/1/unlink_access_token",accountInfo:""+this.apiServer+"/1/account/info",getFile:""+this.fileServer+"/1/files/"+this.fileRoot,postFile:""+this.fileServer+"/1/files/"+this.fileRoot,putFile:""+this.fileServer+"/1/files_put/"+this.fileRoot,metadata:""+this.apiServer+"/1/metadata/"+this.fileRoot,delta:""+this.apiServer+"/1/delta",revisions:""+this.apiServer+"/1/revisions/"+this.fileRoot,restore:""+this.apiServer+"/1/restore/"+this.fileRoot,search:""+this.apiServer+"/1/search/"+this.fileRoot,shares:""+this.apiServer+"/1/shares/"+this.fileRoot,media:""+this.apiServer+"/1/media/"+this.fileRoot,copyRef:""+this.apiServer+"/1/copy_ref/"+this.fileRoot,thumbnails:""+this.fileServer+"/1/thumbnails/"+this.fileRoot,chunkedUpload:""+this.fileServer+"/1/chunked_upload",commitChunkedUpload:""+this.fileServer+"/1/commit_chunked_upload/"+this.fileRoot,fileopsCopy:""+this.apiServer+"/1/fileops/copy",fileopsCreateFolder:""+this.apiServer+"/1/fileops/create_folder",fileopsDelete:""+this.apiServer+"/1/fileops/delete",fileopsMove:""+this.apiServer+"/1/fileops/move"}},r.prototype.authState=null,r.ERROR=0,r.RESET=1,r.REQUEST=2,r.AUTHORIZED=3,r.DONE=4,r.SIGNED_OFF=5,r.prototype.urlEncodePath=function(e){return t.Xhr.urlEncodeValue(this.normalizePath(e)).replace(/%2F/gi,"/")},r.prototype.normalizePath=function(t){var e;if("/"===t.substring(0,1)){for(e=1;"/"===t.substring(e,e+1);)e+=1;return t.substring(e)}return t},r.prototype.requestToken=function(e){var r;return r=new t.Xhr("POST",this.urls.requestToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.authorizeUrl=function(e){var r;return r={oauth_token:e,oauth_callback:this.driver.url()},""+this.urls.authorize+"?"+t.Xhr.urlEncode(r)},r.prototype.getAccessToken=function(e){var r;return r=new t.Xhr("POST",this.urls.accessToken).signWithOauth(this.oauth),this.dispatchXhr(r,e)},r.prototype.dispatchXhr=function(t,e){var r;return t.setCallback(e),t.onError=this.onError,t.prepare(),r=t.xhr,this.onXhr.dispatch(t)&&t.send(),r},r.prototype.defaultApiServer=function(){return"https://api.dropbox.com"},r.prototype.defaultAuthServer=function(){return this.apiServer.replace("api.","www.")},r.prototype.defaultFileServer=function(){return this.apiServer.replace("api.","api-content.")},r.prototype.defaultDownloadServer=function(){return this.apiServer.replace("api.","dl.")},r.prototype.computeCredentials=function(){var t;return t={key:this.oauth.key,sandbox:this.sandbox},this.oauth.secret&&(t.secret=this.oauth.secret),this.oauth.token&&(t.token=this.oauth.token,t.tokenSecret=this.oauth.tokenSecret),this.uid&&(t.uid=this.uid),this.authState!==e.ERROR&&this.authState!==e.RESET&&this.authState!==e.DONE&&this.authState!==e.SIGNED_OFF&&(t.authState=this.authState),this.apiServer!==this.defaultApiServer()&&(t.server=this.apiServer),this.authServer!==this.defaultAuthServer()&&(t.authServer=this.authServer),this.fileServer!==this.defaultFileServer()&&(t.fileServer=this.fileServer),this.downloadServer!==this.defaultDownloadServer()&&(t.downloadServer=this.downloadServer),this._credentials=t},r}(),e=t.Client,t.AuthDriver=function(){function t(){}return t.prototype.url=function(){return"https://some.url"},t.prototype.doAuthorize=function(t,e,r,n){return n("oauth-token")},t.prototype.onAuthStateChange=function(t,e){return e()},t}(),t.Drivers={},t.Drivers.BrowserBase=function(){function t(t){this.rememberUser=(null!=t?t.rememberUser:void 0)||!1,this.scope=(null!=t?t.scope:void 0)||"default"}return t.prototype.onAuthStateChange=function(t,r){var n=this;switch(this.setStorageKey(t),t.authState){case e.RESET:return this.loadCredentials(function(e){return e?e.authState?(t.setCredentials(e),r()):n.rememberUser?(t.setCredentials(e),t.getUserInfo(function(e){return e?(t.reset(),n.forgetCredentials(r)):r()})):(n.forgetCredentials(),r()):r()});case e.REQUEST:return this.storeCredentials(t.credentials(),r);case e.DONE:return this.rememberUser?this.storeCredentials(t.credentials(),r):this.forgetCredentials(r);case e.SIGNED_OFF:return this.forgetCredentials(r);case e.ERROR:return this.forgetCredentials(r);default:return r(),this}},t.prototype.setStorageKey=function(t){return this.storageKey="dropbox-auth:"+this.scope+":"+t.appHash(),this},t.prototype.storeCredentials=function(t,e){return localStorage.setItem(this.storageKey,JSON.stringify(t)),e(),this},t.prototype.loadCredentials=function(t){var e;if(e=localStorage.getItem(this.storageKey),!e)return t(null),this;try{t(JSON.parse(e))}catch(r){t(null)}return this},t.prototype.forgetCredentials=function(t){return localStorage.removeItem(this.storageKey),t(),this},t.currentLocation=function(){return window.location.href},t}(),t.Drivers.Redirect=function(r){function n(t){n.__super__.constructor.call(this,t),this.useQuery=(null!=t?t.useQuery:void 0)||!1,this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)")}return T(n,r),n.prototype.onAuthStateChange=function(t,r){var o,i=this;return o=function(){return function(){return n.__super__.onAuthStateChange.call(i,t,r)}}(),this.setStorageKey(t),t.authState===e.RESET?this.loadCredentials(function(t){return t&&t.authState?t.token===i.locationToken()&&t.authState===e.REQUEST?(t.authState=e.AUTHORIZED,i.storeCredentials(t,o)):i.forgetCredentials(o):o()}):o()},n.prototype.url=function(){return this.receiverUrl},n.prototype.doAuthorize=function(t){return window.location.assign(t)},n.prototype.computeUrl=function(){var e,r,n,o;return o="_dropboxjs_scope="+encodeURIComponent(this.scope),r=t.Drivers.BrowserBase.currentLocation(),-1===r.indexOf("#")?e=null:(n=r.split("#",2),r=n[0],e=n[1]),this.useQuery?r+=-1===r.indexOf("?")?"?"+o:"&"+o:e="?"+o,e?r+"#"+e:r},n.prototype.locationToken=function(){var e,r,n;return e=t.Drivers.BrowserBase.currentLocation(),n="_dropboxjs_scope="+encodeURIComponent(this.scope)+"&",-1===("function"==typeof e.indexOf?e.indexOf(n):void 0)?null:(r=this.tokenRe.exec(e),r?decodeURIComponent(r[2]):null)},n}(t.Drivers.BrowserBase),t.Drivers.Popup=function(r){function n(t){n.__super__.constructor.call(this,t),this.receiverUrl=this.computeUrl(t),this.tokenRe=RegExp("(#|\\?|&)oauth_token=([^]+)(&|#|$)")}return T(n,r),n.prototype.onAuthStateChange=function(t,r){var o,i=this;return o=function(){return function(){return n.__super__.onAuthStateChange.call(i,t,r)}}(),this.setStorageKey(t),t.authState===e.RESET?this.loadCredentials(function(t){return t&&t.authState?i.forgetCredentials(o):o()}):o()},n.prototype.doAuthorize=function(t,e,r,n){return this.listenForMessage(e,n),this.openWindow(t)},n.prototype.url=function(){return this.receiverUrl},n.prototype.computeUrl=function(e){var r;if(e){if(e.receiverUrl)return e.noFragment||-1!==e.receiverUrl.indexOf("#")?e.receiverUrl:e.receiverUrl+"#";if(e.receiverFile)return r=t.Drivers.BrowserBase.currentLocation().split("/"),r[r.length-1]=e.receiverFile,e.noFragment?r.join("/"):r.join("/")+"#"}return t.Drivers.BrowserBase.currentLocation()},n.prototype.openWindow=function(t){return window.open(t,"_dropboxOauthSigninWindow",this.popupWindowSpec(980,700))},n.prototype.popupWindowSpec=function(t,e){var r,n,o,i,s,a,h,u,l,p;return s=null!=(h=window.screenX)?h:window.screenLeft,a=null!=(u=window.screenY)?u:window.screenTop,i=null!=(l=window.outerWidth)?l:document.documentElement.clientWidth,r=null!=(p=window.outerHeight)?p:document.documentElement.clientHeight,n=Math.round(s+(i-t)/2),o=Math.round(a+(r-e)/2.5),s>n&&(n=s),a>o&&(o=a),"width="+t+",height="+e+","+("left="+n+",top="+o)+"dialog=yes,dependent=yes,scrollbars=yes,location=yes"},n.prototype.listenForMessage=function(t,e){var r,n=this;return r=function(o){var i;return i=n.tokenRe.exec(""+o.data),i&&decodeURIComponent(i[2])===t?(window.removeEventListener("message",r),e()):void 0},window.addEventListener("message",r,!1)},n}(t.Drivers.BrowserBase),t.Drivers.NodeServer=function(){function t(t){this.port=(null!=t?t.port:void 0)||8912,this.faviconFile=(null!=t?t.favicon:void 0)||null,this.fs=require("fs"),this.http=require("http"),this.open=require("open"),this.callbacks={},this.urlRe=RegExp("^/oauth_callback\\?"),this.tokenRe=RegExp("(\\?|&)oauth_token=([^&]+)(&|$)"),this.createApp()}return t.prototype.url=function(){return"http://localhost:"+this.port+"/oauth_callback"},t.prototype.doAuthorize=function(t,e,r,n){return this.callbacks[e]=n,this.openBrowser(t)},t.prototype.openBrowser=function(t){if(!t.match(/^https?:\/\//))throw Error("Not a http/https URL: "+t);return this.open(t)},t.prototype.createApp=function(){var t=this;return this.app=this.http.createServer(function(e,r){return t.doRequest(e,r)}),this.app.listen(this.port)},t.prototype.closeServer=function(){return this.app.close()},t.prototype.doRequest=function(t,e){var r,n,o,i=this;return this.urlRe.exec(t.url)&&(n=this.tokenRe.exec(t.url),n&&(o=decodeURIComponent(n[2]),this.callbacks[o]&&(this.callbacks[o](),delete this.callbacks[o]))),r="",t.on("data",function(t){return r+=t}),t.on("end",function(){return i.faviconFile&&"/favicon.ico"===t.url?i.sendFavicon(e):i.closeBrowser(e)})},t.prototype.closeBrowser=function(t){var e;return e='\n\n
Please close this window.
',t.writeHead(200,{"Content-Length":e.length,"Content-Type":"text/html"}),t.write(e),t.end},t.prototype.sendFavicon=function(t){return this.fs.readFile(this.faviconFile,function(e,r){return t.writeHead(200,{"Content-Length":r.length,"Content-Type":"image/x-icon"}),t.write(r),t.end})},t}(),t.EventSource=function(){function t(t){this._cancelable=t&&t.cancelable,this._listeners=[]}return t.prototype.addListener=function(t){if("function"!=typeof t)throw new TypeError("Invalid listener type; expected function");return 0>R.call(this._listeners,t)&&this._listeners.push(t),this},t.prototype.removeListener=function(t){var e,r,n,o,i,s;if(this._listeners.indexOf)r=this._listeners.indexOf(t),-1!==r&&this._listeners.splice(r,1);else for(s=this._listeners,e=o=0,i=s.length;i>o;e=++o)if(n=s[e],n===t){this._listeners.splice(e,1);break}return this},t.prototype.dispatch=function(t){var e,r,n,o,i;for(i=this._listeners,n=0,o=i.length;o>n;n++)if(e=i[n],r=e(t),this._cancelable&&r===!1)return!1;return!0},t}(),c=function(t,e){return h(g(b(t),b(e),t.length,e.length))},d=function(t){return h(S(b(t),t.length))},("undefined"==typeof window||null===window)&&(v=require("crypto"),c=function(t,e){var r;return r=v.createHmac("sha1",e),r.update(t),r.digest("base64")},d=function(t){var e;return e=v.createHash("sha1"),e.update(t),e.digest("base64")}),g=function(t,e,r,n){var o,i,s,a;return e.length>16&&(e=S(e,n)),s=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(909522486^e[i]);return r}(),a=function(){var t,r;for(r=[],i=t=0;16>t;i=++t)r.push(1549556828^e[i]);return r}(),o=S(s.concat(t),64+r),S(a.concat(o),84)},S=function(t,e){var r,n,o,i,s,h,u,l,p,c,d,f,y,v,m,g,S,b;for(t[e>>2]|=1<<31-((3&e)<<3),t[(e+8>>6<<4)+15]=e<<3,g=Array(80),r=1732584193,o=-271733879,s=-1732584194,u=271733878,p=-1009589776,f=0,m=t.length;m>f;){for(n=r,i=o,h=s,l=u,c=p,y=b=0;80>b;y=++b)g[y]=16>y?t[f+y]:w(g[y-3]^g[y-8]^g[y-14]^g[y-16],1),20>y?(d=o&s|~o&u,v=1518500249):40>y?(d=o^s^u,v=1859775393):60>y?(d=o&s|o&u|s&u,v=-1894007588):(d=o^s^u,v=-899497514),S=a(a(w(r,5),d),a(a(p,g[y]),v)),p=u,u=s,s=w(o,30),o=r,r=S;r=a(r,n),o=a(o,i),s=a(s,h),u=a(u,l),p=a(p,c),f+=16}return[r,o,s,u,p]},w=function(t,e){return t<>>32-e},a=function(t,e){var r,n;return n=(65535&t)+(65535&e),r=(t>>16)+(e>>16)+(n>>16),r<<16|65535&n},h=function(t){var e,r,n,o,i;for(o="",e=0,n=4*t.length;n>e;)r=e,i=(255&t[r>>2]>>(3-(3&r)<<3))<<16,r+=1,i|=(255&t[r>>2]>>(3-(3&r)<<3))<<8,r+=1,i|=255&t[r>>2]>>(3-(3&r)<<3),o+=_[63&i>>18],o+=_[63&i>>12],e+=1,o+=e>=n?"=":_[63&i>>6],e+=1,o+=e>=n?"=":_[63&i],e+=1;return o},_="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",b=function(t){var e,r,n,o,i;for(e=[],n=255,r=o=0,i=t.length;i>=0?i>o:o>i;r=i>=0?++o:--o)e[r>>2]|=(t.charCodeAt(r)&n)<<(3-(3&r)<<3);return e},t.Oauth=function(){function e(t){this.key=this.k=null,this.secret=this.s=null,this.token=null,this.tokenSecret=null,this._appHash=null,this.reset(t)}return e.prototype.reset=function(t){var e,r,n,o;if(t.secret)this.k=this.key=t.key,this.s=this.secret=t.secret,this._appHash=null;else if(t.key)this.key=t.key,this.secret=null,n=u(m(this.key).split("|",2)[1]),o=n.split("?",2),e=o[0],r=o[1],this.k=decodeURIComponent(e),this.s=decodeURIComponent(r),this._appHash=null;else if(!this.k)throw Error("No API key supplied");return t.token?this.setToken(t.token,t.tokenSecret):this.setToken(null,"")},e.prototype.setToken=function(e,r){if(e&&!r)throw Error("No secret supplied with the user token");return this.token=e,this.tokenSecret=r||"",this.hmacKey=t.Xhr.urlEncodeValue(this.s)+"&"+t.Xhr.urlEncodeValue(r),null},e.prototype.authHeader=function(e,r,n){var o,i,s,a,h,u;this.addAuthParams(e,r,n),i=[];for(s in n)a=n[s],"oauth_"===s.substring(0,6)&&i.push(s);for(i.sort(),o=[],h=0,u=i.length;u>h;h++)s=i[h],o.push(t.Xhr.urlEncodeValue(s)+'="'+t.Xhr.urlEncodeValue(n[s])+'"'),delete n[s];return"OAuth "+o.join(",")},e.prototype.addAuthParams=function(t,e,r){return this.boilerplateParams(r),r.oauth_signature=this.signature(t,e,r),r},e.prototype.boilerplateParams=function(t){return t.oauth_consumer_key=this.k,t.oauth_nonce=this.nonce(),t.oauth_signature_method="HMAC-SHA1",this.token&&(t.oauth_token=this.token),t.oauth_timestamp=Math.floor(Date.now()/1e3),t.oauth_version="1.0",t},e.prototype.nonce=function(){return Date.now().toString(36)+Math.random().toString(36)},e.prototype.signature=function(e,r,n){var o;return o=e.toUpperCase()+"&"+t.Xhr.urlEncodeValue(r)+"&"+t.Xhr.urlEncodeValue(t.Xhr.urlEncode(n)),c(o,this.hmacKey)},e.prototype.appHash=function(){return this._appHash?this._appHash:this._appHash=d(this.k).replace(/\=/g,"")},e}(),null==Date.now&&(Date.now=function(){return(new Date).getTime()}),m=function(t,e){var r,n,o,i,s,a,h,l,p,c,d,y;for(e?(e=[encodeURIComponent(t),encodeURIComponent(e)].join("?"),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length/2;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(16*(15&t.charCodeAt(2*r))+(15&t.charCodeAt(2*r+1)));return o}()):(c=t.split("|",2),t=c[0],e=c[1],t=u(t),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(t.charCodeAt(r));return o}(),e=u(e)),i=function(){for(y=[],l=0;256>l;l++)y.push(l);return y}.apply(this),a=0,s=p=0;256>p;s=++p)a=(a+i[r]+t[s%t.length])%256,d=[i[a],i[s]],i[s]=d[0],i[a]=d[1];return s=a=0,o=function(){var t,r,o,u;for(u=[],h=t=0,r=e.length;r>=0?r>t:t>r;h=r>=0?++t:--t)s=(s+1)%256,a=(a+i[s])%256,o=[i[a],i[s]],i[s]=o[0],i[a]=o[1],n=i[(i[s]+i[a])%256],u.push(String.fromCharCode((n^e.charCodeAt(h))%256));return u}(),t=function(){var e,n,o;for(o=[],r=e=0,n=t.length;n>=0?n>e:e>n;r=n>=0?++e:--e)o.push(String.fromCharCode(t[r]));return o}(),[f(t.join("")),f(o.join(""))].join("|")},t.PulledChanges=function(){function e(e){var r;this.blankSlate=e.reset||!1,this.cursorTag=e.cursor,this.shouldPullAgain=e.has_more,this.shouldBackOff=!this.shouldPullAgain,this.changes=e.cursor&&e.cursor.length?function(){var n,o,i,s;for(i=e.entries,s=[],n=0,o=i.length;o>n;n++)r=i[n],s.push(t.PullChange.parse(r));return s}():[]}return e.parse=function(e){return e&&"object"==typeof e?new t.PulledChanges(e):e},e.prototype.blankSlate=void 0,e.prototype.cursorTag=void 0,e.prototype.changes=void 0,e.prototype.shouldPullAgain=void 0,e.prototype.shouldBackOff=void 0,e.prototype.cursor=function(){return this.cursorTag},e}(),t.PullChange=function(){function e(e){this.path=e[0],this.stat=t.Stat.parse(e[1]),this.stat?this.wasRemoved=!1:(this.stat=null,this.wasRemoved=!0)}return e.parse=function(e){return e&&"object"==typeof e?new t.PullChange(e):e},e.prototype.path=void 0,e.prototype.wasRemoved=void 0,e.prototype.stat=void 0,e}(),t.PublicUrl=function(){function e(t,e){this.url=t.url,this.expiresAt=new Date(Date.parse(t.expires)),this.isDirect=e===!0?!0:e===!1?!1:"direct"in t?t.direct:864e5>=Date.now()-this.expiresAt,this.isPreview=!this.isDirect,this._json=null}return e.parse=function(e,r){return e&&"object"==typeof e?new t.PublicUrl(e,r):e},e.prototype.url=null,e.prototype.expiresAt=null,e.prototype.isDirect=null,e.prototype.isPreview=null,e.prototype.json=function(){return this._json||(this._json={url:this.url,expires:""+this.expiresAt,direct:this.isDirect})},e}(),t.CopyReference=function(){function e(t){"object"==typeof t?(this.tag=t.copy_ref,this.expiresAt=new Date(Date.parse(t.expires)),this._json=t):(this.tag=t,this.expiresAt=new Date(1e3*Math.ceil(Date.now()/1e3)),this._json=null)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.CopyReference(e)},e.prototype.tag=null,e.prototype.expiresAt=null,e.prototype.json=function(){return this._json||(this._json={copy_ref:this.tag,expires:""+this.expiresAt})},e}(),t.Stat=function(){function e(t){var e,r,n,o;switch(this._json=t,this.path=t.path,"/"!==this.path.substring(0,1)&&(this.path="/"+this.path),e=this.path.length-1,e>=0&&"/"===this.path.substring(e)&&(this.path=this.path.substring(0,e)),r=this.path.lastIndexOf("/"),this.name=this.path.substring(r+1),this.isFolder=t.is_dir||!1,this.isFile=!this.isFolder,this.isRemoved=t.is_deleted||!1,this.typeIcon=t.icon,this.modifiedAt=(null!=(n=t.modified)?n.length:void 0)?new Date(Date.parse(t.modified)):null,this.clientModifiedAt=(null!=(o=t.client_mtime)?o.length:void 0)?new Date(Date.parse(t.client_mtime)):null,t.root){case"dropbox":this.inAppFolder=!1;break;case"app_folder":this.inAppFolder=!0;break;default:this.inAppFolder=null}this.size=t.bytes||0,this.humanSize=t.size||"",this.hasThumbnail=t.thumb_exists||!1,this.isFolder?(this.versionTag=t.hash,this.mimeType=t.mime_type||"inode/directory"):(this.versionTag=t.rev,this.mimeType=t.mime_type||"application/octet-stream")}return e.parse=function(e){return e&&"object"==typeof e?new t.Stat(e):e},e.prototype.path=null,e.prototype.name=null,e.prototype.inAppFolder=null,e.prototype.isFolder=null,e.prototype.isFile=null,e.prototype.isRemoved=null,e.prototype.typeIcon=null,e.prototype.versionTag=null,e.prototype.mimeType=null,e.prototype.size=null,e.prototype.humanSize=null,e.prototype.hasThumbnail=null,e.prototype.modifiedAt=null,e.prototype.clientModifiedAt=null,e.prototype.json=function(){return this._json
+},e}(),t.UploadCursor=function(){function e(t){this.replace(t)}return e.parse=function(e){return!e||"object"!=typeof e&&"string"!=typeof e?e:new t.UploadCursor(e)},e.prototype.tag=null,e.prototype.offset=null,e.prototype.expiresAt=null,e.prototype.json=function(){return this._json||(this._json={upload_id:this.tag,offset:this.offset,expires:""+this.expiresAt})},e.prototype.replace=function(t){return"object"==typeof t?(this.tag=t.upload_id||null,this.offset=t.offset||0,this.expiresAt=new Date(Date.parse(t.expires)||Date.now()),this._json=t):(this.tag=t||null,this.offset=0,this.expiresAt=new Date(1e3*Math.floor(Date.now()/1e3)),this._json=null),this},e}(),t.UserInfo=function(){function e(t){var e;this._json=t,this.name=t.display_name,this.email=t.email,this.countryCode=t.country||null,this.uid=""+t.uid,t.public_app_url?(this.publicAppUrl=t.public_app_url,e=this.publicAppUrl.length-1,e>=0&&"/"===this.publicAppUrl.substring(e)&&(this.publicAppUrl=this.publicAppUrl.substring(0,e))):this.publicAppUrl=null,this.referralUrl=t.referral_link,this.quota=t.quota_info.quota,this.privateBytes=t.quota_info.normal||0,this.sharedBytes=t.quota_info.shared||0,this.usedQuota=this.privateBytes+this.sharedBytes}return e.parse=function(e){return e&&"object"==typeof e?new t.UserInfo(e):e},e.prototype.name=null,e.prototype.email=null,e.prototype.countryCode=null,e.prototype.uid=null,e.prototype.referralUrl=null,e.prototype.publicAppUrl=null,e.prototype.quota=null,e.prototype.usedQuota=null,e.prototype.privateBytes=null,e.prototype.sharedBytes=null,e.prototype.json=function(){return this._json},e}(),"undefined"!=typeof window&&null!==window?(!window.XDomainRequest||"withCredentials"in new XMLHttpRequest?(s=window.XMLHttpRequest,i=!1,n=-1===window.navigator.userAgent.indexOf("Firefox")):(s=window.XDomainRequest,i=!0,n=!1),o=!0):(s=require("xmlhttprequest").XMLHttpRequest,i=!1,n=!1,o=!1),"undefined"==typeof Uint8Array?r=null:Object.getPrototypeOf?r=Object.getPrototypeOf(Object.getPrototypeOf(new Uint8Array(0))).constructor:Object.__proto__&&(r=new Uint8Array(0).__proto__.__proto__.constructor),t.Xhr=function(){function e(t,e){this.method=t,this.isGet="GET"===this.method,this.url=e,this.headers={},this.params=null,this.body=null,this.preflight=!(this.isGet||"POST"===this.method),this.signed=!1,this.responseType=null,this.callback=null,this.xhr=null,this.onError=null}return e.Request=s,e.ieXdr=i,e.canSendForms=n,e.doesPreflight=o,e.ArrayBufferView=r,e.prototype.xhr=null,e.prototype.onError=null,e.prototype.setParams=function(t){if(this.signed)throw Error("setParams called after addOauthParams or addOauthHeader");if(this.params)throw Error("setParams cannot be called twice");return this.params=t,this},e.prototype.setCallback=function(t){return this.callback=t,this},e.prototype.signWithOauth=function(e){return t.Xhr.ieXdr||t.Xhr.doesPreflight&&!this.preflight?this.addOauthParams(e):this.addOauthHeader(e)},e.prototype.addOauthParams=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),t.addAuthParams(this.method,this.url,this.params),this.signed=!0,this},e.prototype.addOauthHeader=function(t){if(this.signed)throw Error("Request already has an OAuth signature");return this.params||(this.params={}),this.signed=!0,this.setHeader("Authorization",t.authHeader(this.method,this.url,this.params))},e.prototype.setBody=function(t){if(this.isGet)throw Error("setBody cannot be called on GET requests");if(null!==this.body)throw Error("Request already has a body");return this.preflight||"undefined"!=typeof FormData&&t instanceof FormData||(this.preflight=!0),this.body=t,this},e.prototype.setResponseType=function(t){return this.responseType=t,this},e.prototype.setHeader=function(t,e){var r;if(this.headers[t])throw r=this.headers[t],Error("HTTP header "+t+" already set to "+r);if("Content-Type"===t)throw Error("Content-Type is automatically computed based on setBody");return this.preflight=!0,this.headers[t]=e,this},e.prototype.setFileField=function(e,r,n,o){var i,s;if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("setFileField cannot be called on GET requests");return"object"==typeof n&&"undefined"!=typeof Blob?("undefined"!=typeof ArrayBuffer&&null!==ArrayBuffer&&n instanceof ArrayBuffer&&(n=new Uint8Array(n)),t.Xhr.ArrayBufferView&&n instanceof t.Xhr.ArrayBufferView&&(o||(o="application/octet-stream"),n=new Blob([n],{type:o})),"undefined"!=typeof File&&n instanceof File&&(n=new Blob([n],{type:n.type})),s=n instanceof Blob):s=!1,s?(this.body=new FormData,this.body.append(e,n,r)):(o||(o="application/octet-stream"),i=this.multipartBoundary(),this.headers["Content-Type"]="multipart/form-data; boundary="+i,this.body=["--",i,"\r\n",'Content-Disposition: form-data; name="',e,'"; filename="',r,'"\r\n',"Content-Type: ",o,"\r\n","Content-Transfer-Encoding: binary\r\n\r\n",n,"\r\n","--",i,"--","\r\n"].join(""))},e.prototype.multipartBoundary=function(){return[Date.now().toString(36),Math.random().toString(36)].join("----")},e.prototype.paramsToUrl=function(){var e;return this.params&&(e=t.Xhr.urlEncode(this.params),0!==e.length&&(this.url=[this.url,"?",e].join("")),this.params=null),this},e.prototype.paramsToBody=function(){if(this.params){if(null!==this.body)throw Error("Request already has a body");if(this.isGet)throw Error("paramsToBody cannot be called on GET requests");this.headers["Content-Type"]="application/x-www-form-urlencoded",this.body=t.Xhr.urlEncode(this.params),this.params=null}return this},e.prototype.prepare=function(){var e,r,n,o,i=this;if(r=t.Xhr.ieXdr,this.isGet||null!==this.body||r?(this.paramsToUrl(),null!==this.body&&"string"==typeof this.body&&(this.headers["Content-Type"]="text/plain; charset=utf8")):this.paramsToBody(),this.xhr=new t.Xhr.Request,r?(this.xhr.onload=function(){return i.onXdrLoad()},this.xhr.onerror=function(){return i.onXdrError()},this.xhr.ontimeout=function(){return i.onXdrError()},this.xhr.onprogress=function(){}):this.xhr.onreadystatechange=function(){return i.onReadyStateChange()},this.xhr.open(this.method,this.url,!0),!r){o=this.headers;for(e in o)E.call(o,e)&&(n=o[e],this.xhr.setRequestHeader(e,n))}return this.responseType&&("b"===this.responseType?this.xhr.overrideMimeType&&this.xhr.overrideMimeType("text/plain; charset=x-user-defined"):this.xhr.responseType=this.responseType),this},e.prototype.send=function(e){var r;if(this.callback=e||this.callback,null!==this.body){r=this.body,t.Xhr.ArrayBufferView&&r instanceof ArrayBuffer&&(r=new Uint8Array(r));try{this.xhr.send(r)}catch(n){if(!("undefined"!=typeof Blob&&t.Xhr.ArrayBufferView&&r instanceof t.Xhr.ArrayBufferView))throw n;r=new Blob([r],{type:"application/octet-stream"}),this.xhr.send(r)}}else this.xhr.send();return this},e.urlEncode=function(t){var e,r,n;e=[];for(r in t)n=t[r],e.push(this.urlEncodeValue(r)+"="+this.urlEncodeValue(n));return e.sort().join("&")},e.urlEncodeValue=function(t){return encodeURIComponent(""+t).replace(/\!/g,"%21").replace(/'/g,"%27").replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\*/g,"%2A")},e.urlDecode=function(t){var e,r,n,o,i,s;for(r={},s=t.split("&"),o=0,i=s.length;i>o;o++)n=s[o],e=n.split("="),r[decodeURIComponent(e[0])]=decodeURIComponent(e[1]);return r},e.prototype.onReadyStateChange=function(){var e,r,n,o,i,s,a,h,u;if(4!==this.xhr.readyState)return!0;if(200>this.xhr.status||this.xhr.status>=300)return e=new t.ApiError(this.xhr,this.method,this.url),this.onError&&this.onError.dispatch(e),this.callback(e),!0;if(s=this.xhr.getResponseHeader("x-dropbox-metadata"),null!=s?s.length:void 0)try{i=JSON.parse(s)}catch(l){i=void 0}else i=void 0;if(this.responseType){if("b"===this.responseType){for(n=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,r=[],o=h=0,u=n.length;u>=0?u>h:h>u;o=u>=0?++h:--h)r.push(String.fromCharCode(255&n.charCodeAt(o)));a=r.join(""),this.callback(null,a,i)}else this.callback(null,this.xhr.response,i);return!0}switch(a=null!=this.xhr.responseText?this.xhr.responseText:this.xhr.response,this.xhr.getResponseHeader("Content-Type")){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(a),i);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(a),i);break;default:this.callback(null,a,i)}return!0},e.prototype.onXdrLoad=function(){var e;switch(e=this.xhr.responseText,this.xhr.contentType){case"application/x-www-form-urlencoded":this.callback(null,t.Xhr.urlDecode(e),void 0);break;case"application/json":case"text/javascript":this.callback(null,JSON.parse(e),void 0);break;default:this.callback(null,e,void 0)}return!0},e.prototype.onXdrError=function(){var e;return e=new t.ApiError(this.xhr,this.method,this.url),this.onError&&this.onError.dispatch(e),this.callback(e),!0},e}(),null!=("undefined"!=typeof module&&null!==module?module.exports:void 0))module.exports=t;else{if("undefined"==typeof window||null===window)throw Error("This library only supports node.js and modern browsers.");window.Dropbox=t}t.atob=u,t.btoa=f,t.hmac=c,t.sha1=d,t.encodeKey=m}).call(this);
\ No newline at end of file
diff --git a/lib/client/storage/dropbox/samples/checkbox.js/public/lib/jquery.js b/lib/client/storage/dropbox/samples/checkbox.js/public/lib/jquery.js
new file mode 100644
index 00000000..83589daa
--- /dev/null
+++ b/lib/client/storage/dropbox/samples/checkbox.js/public/lib/jquery.js
@@ -0,0 +1,2 @@
+/*! jQuery v1.8.3 jquery.com | jquery.org/license */
+(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t a ",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML=" ",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML=" ";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="
",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r ",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="
",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML=" ",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/ ]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""," "],legend:[1,""," "],thead:[1,""],tr:[2,""],td:[3,""],col:[2,""],area:[1,""," "],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X ","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>$2>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>$2>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/
+ Please close this window.
+ """
+ response.writeHead(200,
+ {'Content-Length': closeHtml.length, 'Content-Type': 'text/html' })
+ response.write closeHtml
+ response.end
+
+ # Renders the favicon file.
+ sendFavicon: (response) ->
+ @fs.readFile @faviconFile, (error, data) ->
+ response.writeHead(200,
+ { 'Content-Length': data.length, 'Content-Type': 'image/x-icon' })
+ response.write data
+ response.end
diff --git a/lib/client/storage/dropbox/src/drivers.coffee b/lib/client/storage/dropbox/src/drivers.coffee
index d48184e4..de555551 100644
--- a/lib/client/storage/dropbox/src/drivers.coffee
+++ b/lib/client/storage/dropbox/src/drivers.coffee
@@ -57,421 +57,3 @@ class Dropbox.AuthDriver
# Namespace for authentication drivers.
Dropbox.Drivers = {}
-
-# Base class for drivers that run in the browser.
-#
-# Inheriting from this class makes a driver use HTML5 localStorage to preserve
-# OAuth tokens across page reloads.
-class Dropbox.Drivers.BrowserBase
- # Sets up the OAuth driver.
- #
- # Subclasses should pass the options object they receive to the superclass
- # constructor.
- #
- # @param {?Object} options the advanced settings below
- # @option options {Boolean} rememberUser if true, the user's OAuth tokens are
- # saved in localStorage; if you use this, you MUST provide a UI item that
- # calls signOut() on Dropbox.Client, to let the user "log out" of the
- # application
- # @option options {String} scope embedded in the localStorage key that holds
- # the authentication data; useful for having multiple OAuth tokens in a
- # single application
- constructor: (options) ->
- @rememberUser = options?.rememberUser or false
- @scope = options?.scope or 'default'
-
- # The magic happens here.
- onAuthStateChange: (client, callback) ->
- @setStorageKey client
-
- switch client.authState
- when DropboxClient.RESET
- @loadCredentials (credentials) =>
- return callback() unless credentials
-
- if credentials.authState # Incomplete authentication.
- client.setCredentials credentials
- return callback()
-
- # There is an old access token. Only use it if the app supports
- # logout.
- unless @rememberUser
- @forgetCredentials()
- return callback()
-
- # Verify that the old access token still works.
- client.setCredentials credentials
- client.getUserInfo (error) =>
- if error
- client.reset()
- @forgetCredentials callback
- else
- callback()
- when DropboxClient.REQUEST
- @storeCredentials client.credentials(), callback
- when DropboxClient.DONE
- if @rememberUser
- return @storeCredentials(client.credentials(), callback)
- @forgetCredentials callback
- when DropboxClient.SIGNED_OFF
- @forgetCredentials callback
- when DropboxClient.ERROR
- @forgetCredentials callback
- else
- callback()
- @
-
- # Computes the @storageKey used by loadCredentials and forgetCredentials.
- #
- # @private
- # This is called by onAuthStateChange.
- #
- # @param {Dropbox.Client} client the client instance that is running the
- # authorization process
- # @return {Dropbox.Driver} this, for easy call chaining
- setStorageKey: (client) ->
- # NOTE: the storage key is dependent on the app hash so that multiple apps
- # hosted off the same server don't step on eachother's toes
- @storageKey = "dropbox-auth:#{@scope}:#{client.appHash()}"
- @
-
- # Stores a Dropbox.Client's credentials to localStorage.
- #
- # @private
- # onAuthStateChange calls this method during the authentication flow.
- #
- # @param {Object} credentials the result of a Drobpox.Client#credentials call
- # @param {function()} callback called when the storing operation is complete
- # @return {Dropbox.Drivers.BrowserBase} this, for easy call chaining
- storeCredentials: (credentials, callback) ->
- localStorage.setItem @storageKey, JSON.stringify(credentials)
- callback()
- @
-
- # Retrieves a token and secret from localStorage.
- #
- # @private
- # onAuthStateChange calls this method during the authentication flow.
- #
- # @param {function(?Object)} callback supplied with the credentials object
- # stored by a previous call to
- # Dropbox.Drivers.BrowserBase#storeCredentials; null if no credentials were
- # stored, or if the previously stored credentials were deleted
- # @return {Dropbox.Drivers.BrowserBase} this, for easy call chaining
- loadCredentials: (callback) ->
- jsonString = localStorage.getItem @storageKey
- unless jsonString
- callback null
- return @
-
- try
- callback JSON.parse(jsonString)
- catch e
- # Parse errors.
- callback null
- @
-
- # Deletes information previously stored by a call to storeToken.
- #
- # @private
- # onAuthStateChange calls this method during the authentication flow.
- #
- # @param {function()} callback called after the credentials are deleted
- # @return {Dropbox.Drivers.BrowserBase} this, for easy call chaining
- forgetCredentials: (callback) ->
- localStorage.removeItem @storageKey
- callback()
- @
-
- # Wrapper for window.location, for testing purposes.
- #
- # @return {String} the current page's URL
- @currentLocation: ->
- window.location.href
-
-# OAuth driver that uses a redirect and localStorage to complete the flow.
-class Dropbox.Drivers.Redirect extends Dropbox.Drivers.BrowserBase
- # Sets up the redirect-based OAuth driver.
- #
- # @param {?Object} options the advanced settings below
- # @option options {Boolean} useQuery if true, the page will receive OAuth
- # data as query parameters; by default, the page receives OAuth data in
- # the fragment part of the URL (the string following the #,
- # available as document.location.hash), to avoid confusing the server
- # generating the page
- # @option options {Boolean} rememberUser if true, the user's OAuth tokens are
- # saved in localStorage; if you use this, you MUST provide a UI item that
- # calls signOut() on Dropbox.Client, to let the user "log out" of the
- # application
- # @option options {String} scope embedded in the localStorage key that holds
- # the authentication data; useful for having multiple OAuth tokens in a
- # single application
- constructor: (options) ->
- super options
- @useQuery = options?.useQuery or false
- @receiverUrl = @computeUrl options
- @tokenRe = new RegExp "(#|\\?|&)oauth_token=([^]+)(&|#|$)"
-
- # Forwards the authentication process from REQUEST to AUTHORIZED on redirect.
- onAuthStateChange: (client, callback) ->
- superCall = do => => super client, callback
- @setStorageKey client
- if client.authState is DropboxClient.RESET
- @loadCredentials (credentials) =>
- if credentials and credentials.authState # Incomplete authentication.
- if credentials.token is @locationToken() and
- credentials.authState is DropboxClient.REQUEST
- # locationToken matched, so the redirect happened
- credentials.authState = DropboxClient.AUTHORIZED
- return @storeCredentials credentials, superCall
- else
- # The authentication process broke down, start over.
- return @forgetCredentials superCall
- superCall()
- else
- superCall()
-
- # URL of the current page, since the user will be sent right back.
- url: ->
- @receiverUrl
-
- # Redirects to the authorize page.
- doAuthorize: (authUrl) ->
- window.location.assign authUrl
-
- # Pre-computes the return value of url.
- computeUrl: ->
- querySuffix = "_dropboxjs_scope=#{encodeURIComponent @scope}"
- location = Dropbox.Drivers.BrowserBase.currentLocation()
- if location.indexOf('#') is -1
- fragment = null
- else
- locationPair = location.split '#', 2
- location = locationPair[0]
- fragment = locationPair[1]
- if @useQuery
- if location.indexOf('?') is -1
- location += "?#{querySuffix}" # No query string in the URL.
- else
- location += "{querySuffix}" # The URL already has a query string.
- else
- fragment = "?#{querySuffix}"
-
- if fragment
- location + '#' + fragment
- else
- location
-
- # Figures out if the user completed the OAuth flow based on the current URL.
- #
- # @return {?String} the OAuth token that the user just authorized, or null if
- # the user accessed this directly, without having authorized a token
- locationToken: ->
- location = Dropbox.Drivers.BrowserBase.currentLocation()
-
- # Check for the scope.
- scopePattern = "_dropboxjs_scope=#{encodeURIComponent @scope}&"
- return null if location.indexOf?(scopePattern) is -1
-
- # Extract the token.
- match = @tokenRe.exec location
- if match then decodeURIComponent(match[2]) else null
-
-# OAuth driver that uses a popup window and postMessage to complete the flow.
-class Dropbox.Drivers.Popup extends Dropbox.Drivers.BrowserBase
- # Sets up a popup-based OAuth driver.
- #
- # @param {?Object} options one of the settings below; leave out the argument
- # to use the current location for redirecting
- # @option options {Boolean} rememberUser if true, the user's OAuth tokens are
- # saved in localStorage; if you use this, you MUST provide a UI item that
- # calls signOut() on Dropbox.Client, to let the user "log out" of the
- # application
- # @option options {String} scope embedded in the localStorage key that holds
- # the authentication data; useful for having multiple OAuth tokens in a
- # single application
- # @option options {String} receiverUrl URL to the page that receives the
- # /authorize redirect and performs the postMessage
- # @option options {Boolean} noFragment if true, the receiverUrl will be used
- # as given; by default, a hash "#" is appended to URLs that don't have
- # one, so the OAuth token is received as a URL fragment and does not hit
- # the file server
- # @option options {String} receiverFile the URL to the receiver page will be
- # computed by replacing the file name (everything after the last /) of
- # the current location with this parameter's value
- constructor: (options) ->
- super options
- @receiverUrl = @computeUrl options
- @tokenRe = new RegExp "(#|\\?|&)oauth_token=([^]+)(&|#|$)"
-
- # Removes credentials stuck in the REQUEST stage.
- onAuthStateChange: (client, callback) ->
- superCall = do => => super client, callback
- @setStorageKey client
- if client.authState is DropboxClient.RESET
- @loadCredentials (credentials) ->
- if credentials and credentials.authState # Incomplete authentication.
- # The authentication process broke down, start over.
- return @forgetCredentials superCall
- superCall()
- else
- superCall()
-
- # Shows the authorization URL in a pop-up, waits for it to send a message.
- doAuthorize: (authUrl, token, tokenSecret, callback) ->
- @listenForMessage token, callback
- @openWindow authUrl
-
- # URL of the redirect receiver page, which posts a message back to this page.
- url: ->
- @receiverUrl
-
- # Pre-computes the return value of url.
- computeUrl: (options) ->
- if options
- if options.receiverUrl
- if options.noFragment or options.receiverUrl.indexOf('#') isnt -1
- return options.receiverUrl
- else
- return options.receiverUrl + '#'
- else if options.receiverFile
- fragments = Dropbox.Drivers.BrowserBase.currentLocation().split '/'
- fragments[fragments.length - 1] = options.receiverFile
- if options.noFragment
- return fragments.join('/')
- else
- return fragments.join('/') + '#'
- Dropbox.Drivers.BrowserBase.currentLocation()
-
- # Creates a popup window.
- #
- # @param {String} url the URL that will be loaded in the popup window
- # @return {?DOMRef} reference to the opened window, or null if the call
- # failed
- openWindow: (url) ->
- window.open url, '_dropboxOauthSigninWindow', @popupWindowSpec(980, 700)
-
- # Spec string for window.open to create a nice popup.
- #
- # @param {Number} popupWidth the desired width of the popup window
- # @param {Number} popupHeight the desired height of the popup window
- # @return {String} spec string for the popup window
- popupWindowSpec: (popupWidth, popupHeight) ->
- # Metrics for the current browser window.
- x0 = window.screenX ? window.screenLeft
- y0 = window.screenY ? window.screenTop
- width = window.outerWidth ? document.documentElement.clientWidth
- height = window.outerHeight ? document.documentElement.clientHeight
-
- # Computed popup window metrics.
- popupLeft = Math.round x0 + (width - popupWidth) / 2
- popupTop = Math.round y0 + (height - popupHeight) / 2.5
- popupLeft = x0 if popupLeft < x0
- popupTop = y0 if popupTop < y0
-
- # The specification string.
- "width=#{popupWidth},height=#{popupHeight}," +
- "left=#{popupLeft},top=#{popupTop}" +
- 'dialog=yes,dependent=yes,scrollbars=yes,location=yes'
-
- # Listens for a postMessage from a previously opened popup window.
- #
- # @param {String} token the token string that must be received from the popup
- # window
- # @param {function()} called when the received message matches the token
- listenForMessage: (token, callback) ->
- listener = (event) =>
- match = @tokenRe.exec event.data.toString()
- if match and decodeURIComponent(match[2]) is token
- window.removeEventListener 'message', listener
- callback()
- window.addEventListener 'message', listener, false
-
-
-# OAuth driver that redirects the browser to a node app to complete the flow.
-#
-# This is useful for testing node.js libraries and applications.
-class Dropbox.Drivers.NodeServer
- # Starts up the node app that intercepts the browser redirect.
- #
- # @param {?Object} options one or more of the options below
- # @option options {Number} port the number of the TCP port that will receive
- # HTTP requests
- # @param {String} faviconFile the path to a file that will be served at
- # /favicon.ico
- constructor: (options) ->
- @port = options?.port or 8912
- @faviconFile = options?.favicon or null
- # Calling require in the constructor because this doesn't work in browsers.
- @fs = require 'fs'
- @http = require 'http'
- @open = require 'open'
-
- @callbacks = {}
- @urlRe = new RegExp "^/oauth_callback\\?"
- @tokenRe = new RegExp "(\\?|&)oauth_token=([^&]+)(&|$)"
- @createApp()
-
- # URL to the node.js OAuth callback handler.
- url: ->
- "http://localhost:#{@port}/oauth_callback"
-
- # Opens the token
- doAuthorize: (authUrl, token, tokenSecret, callback) ->
- @callbacks[token] = callback
- @openBrowser authUrl
-
- # Opens the given URL in a browser.
- openBrowser: (url) ->
- unless url.match /^https?:\/\//
- throw new Error("Not a http/https URL: #{url}")
- @open url
-
- # Creates and starts up an HTTP server that will intercept the redirect.
- createApp: ->
- @app = @http.createServer (request, response) =>
- @doRequest request, response
- @app.listen @port
-
- # Shuts down the HTTP server.
- #
- # The driver will become unusable after this call.
- closeServer: ->
- @app.close()
-
- # Reads out an /authorize callback.
- doRequest: (request, response) ->
- if @urlRe.exec request.url
- match = @tokenRe.exec request.url
- if match
- token = decodeURIComponent match[2]
- if @callbacks[token]
- @callbacks[token]()
- delete @callbacks[token]
- data = ''
- request.on 'data', (dataFragment) -> data += dataFragment
- request.on 'end', =>
- if @faviconFile and (request.url is '/favicon.ico')
- @sendFavicon response
- else
- @closeBrowser response
-
- # Renders a response that will close the browser window used for OAuth.
- closeBrowser: (response) ->
- closeHtml = """
-
-
- Please close this window.
- """
- response.writeHead(200,
- {'Content-Length': closeHtml.length, 'Content-Type': 'text/html' })
- response.write closeHtml
- response.end
-
- # Renders the favicon file.
- sendFavicon: (response) ->
- @fs.readFile @faviconFile, (error, data) ->
- response.writeHead(200,
- { 'Content-Length': data.length, 'Content-Type': 'image/x-icon' })
- response.write data
- response.end
diff --git a/lib/client/storage/dropbox/src/event_source.coffee b/lib/client/storage/dropbox/src/event_source.coffee
new file mode 100644
index 00000000..b87a3d3c
--- /dev/null
+++ b/lib/client/storage/dropbox/src/event_source.coffee
@@ -0,0 +1,71 @@
+# Event dispatch following a publisher-subscriber (PubSub) model.
+class Dropbox.EventSource
+ # Sets up an event source (publisher).
+ #
+ # @param {?Object} options one or more of the options below
+ # @option options {Boolean} cancelable if true,
+ constructor: (options) ->
+ @_cancelable = options and options.cancelable
+ @_listeners = []
+
+ # Registers a listener (subscriber) to events coming from this source.
+ #
+ # This is a simplified version of the addEventListener DOM API. Listeners
+ # must be functions, and they can be removed by calling removeListener.
+ #
+ # This method is idempotent, so a function will not be added to the list of
+ # listeners if was previously added.
+ #
+ # @param {function(Object)} listener called every time an event is fired; if
+ # the event is cancelable, the function can return false to cancel the
+ # event, or any other value to allow it to propagate; the return value is
+ # ignored for non-cancelable events
+ # @return {Dropbox.EventSource} this, for easy call chaining
+ addListener: (listener) ->
+ unless typeof listener is 'function'
+ throw new TypeError 'Invalid listener type; expected function'
+ unless listener in @_listeners
+ @_listeners.push listener
+ @
+
+ # Un-registers a listener (subscriber) previously added by addListener.
+ #
+ # This is a simplified version of the removeEventListener DOM API. The
+ # listener must be exactly the same object supplied to addListener.
+ #
+ # This method is idempotent, so it will fail silently if the given listener
+ # is not registered as a subscriber.
+ #
+ # @param {function(Object)} listener function that was previously passed in
+ # an addListener call
+ # @return {Dropbox.EventSource} this, for easy call chaining
+ removeListener: (listener) ->
+ if @_listeners.indexOf
+ # IE9+
+ index = @_listeners.indexOf listener
+ @_listeners.splice index, 1 if index isnt -1
+ else
+ # IE8 doesn't implement Array#indexOf in ES5.
+ for subscriber, i in @_listeners
+ if subscriber is listener
+ @_listeners.splice i, 1
+ break
+ @
+
+
+ # Informs the listeners (subscribers) that an event occurred.
+ #
+ # Event sources configured for non-cancelable events call all listeners in an
+ # unspecified order. Sources configured for cancelable events stop calling
+ # listeners as soon as one listener returns false value.
+ #
+ # @param {Object} event passed to all the registered listeners
+ # @return {Boolean} sources of cancelable events return false if the event
+ # was canceled and true otherwise; sources of non-cancelable events always
+ # return true
+ dispatch: (event) ->
+ for listener in @_listeners
+ returnValue = listener event
+ if @_cancelable and returnValue is false
+ return false
+ true
diff --git a/lib/client/storage/dropbox/src/pulled_changes.coffee b/lib/client/storage/dropbox/src/pulled_changes.coffee
index 0a324d24..6bf7ebcd 100644
--- a/lib/client/storage/dropbox/src/pulled_changes.coffee
+++ b/lib/client/storage/dropbox/src/pulled_changes.coffee
@@ -37,13 +37,19 @@ class Dropbox.PulledChanges
# least 5 miuntes before issuing another pullChanges request
shouldBackOff: undefined
+ # Serializable representation of the pull cursor inside this object.
+ #
+ # @return {String} an ASCII string that can be passed to pullChanges instead
+ # of this PulledChanges instance
+ cursor: -> @cursorTag
+
# Creates a new Dropbox.PulledChanges instance from a /delta API call result.
#
# @private
# This constructor is used by Dropbox.PulledChanges, and should not be called
# directly.
#
- # @param {?Object} deltaInfo the parsed JSON of a /delta API call result
+ # @param {Object} deltaInfo the parsed JSON of a /delta API call result
constructor: (deltaInfo) ->
@blankSlate = deltaInfo.reset or false
@cursorTag = deltaInfo.cursor
@@ -88,7 +94,7 @@ class Dropbox.PullChange
# This constructor is used by Dropbox.PullChange.parse, and should not be
# called directly.
#
- # @param {?Object} entry the parsed JSON of a single entry in a /delta API
+ # @param {Object} entry the parsed JSON of a single entry in a /delta API
# call result
constructor: (entry) ->
@path = entry[0]
diff --git a/lib/client/storage/dropbox/src/references.coffee b/lib/client/storage/dropbox/src/references.coffee
index f5e62437..d7602c5f 100644
--- a/lib/client/storage/dropbox/src/references.coffee
+++ b/lib/client/storage/dropbox/src/references.coffee
@@ -2,8 +2,8 @@
class Dropbox.PublicUrl
# Creates a PublicUrl instance from a raw API response.
#
- # @param {?Object} urlData the parsed JSON describing a public URL
- # @param {Boolean} isDirect true if this is a direct download link, false if
+ # @param {?Object, ?String} urlData the parsed JSON describing a public URL
+ # @param {?Boolean} isDirect true if this is a direct download link, false if
# is a file / folder preview link
# @return {?Dropbox.PublicUrl} a PublicUrl instance wrapping the given public
# link info; parameters that don't look like parsed JSON are returned as
@@ -15,19 +15,28 @@ class Dropbox.PublicUrl
urlData
# @property {String} the public URL
- url: undefined
+ url: null
# @property {Date} after this time, the URL is not usable
- expiresAt: undefined
+ expiresAt: null
# @property {Boolean} true if this is a direct download URL, false for URLs to
# preview pages in the Dropbox web app; folders do not have direct link
#
- isDirect: undefined
+ isDirect: null
# @property {Boolean} true if this is URL points to a file's preview page in
# Dropbox, false for direct links
- isPreview: undefined
+ isPreview: null
+
+ # JSON representation of this file / folder's metadata
+ #
+ # @return {Object} conforms to the JSON restrictions; can be passed to
+ # Dropbox.PublicUrl#parse to obtain an identical PublicUrl instance
+ json: ->
+ # HACK: this can break if the Dropbox API ever decides to use 'direct' in
+ # its link info
+ @_json ||= url: @url, expires: @expiresAt.toString(), direct: @isDirect
# Creates a PublicUrl instance from a raw API response.
#
@@ -47,14 +56,25 @@ class Dropbox.PublicUrl
else if isDirect is false
@isDirect = false
else
- @isDirect = Date.now() - @expiresAt <= 86400000 # 1 day
+ # HACK: this can break if the Dropbox API ever decides to use 'direct' in
+ # its link info; unfortunately, there's no elegant way to guess
+ # between direct download URLs and preview URLs
+ if 'direct' of urlData
+ @isDirect = urlData.direct
+ else
+ @isDirect = Date.now() - @expiresAt <= 86400000 # 1 day
@isPreview = !@isDirect
+ # The JSON representation is created on-demand, to avoid unnecessary object
+ # creation.
+ # We can't use the original JSON object because we add a 'direct' field.
+ @_json = null
+
# Reference to a file that can be used to make a copy across users' Dropboxes.
class Dropbox.CopyReference
# Creates a CopyReference instance from a raw reference or API response.
#
- # @param {?Object, ?String} refData the parsed JSON descring a copy
+ # @param {?Object, ?String} refData the parsed JSON describing a copy
# reference, or the reference string
@parse: (refData) ->
if refData and (typeof refData is 'object' or typeof refData is 'string')
@@ -63,10 +83,20 @@ class Dropbox.CopyReference
refData
# @property {String} the raw reference, for use with Dropbox APIs
- tag: undefined
+ tag: null
# @property {Date} deadline for using the reference in a copy operation
- expiresAt: undefined
+ expiresAt: null
+
+ # JSON representation of this file / folder's metadata
+ #
+ # @return {Object} conforms to the JSON restrictions; can be passed to
+ # Dropbox.CopyReference#parse to obtain an identical CopyReference instance
+ json: ->
+ # NOTE: the assignment only occurs if the CopyReference was built around a
+ # string; CopyReferences parsed from API responses hold onto the
+ # original JSON
+ @_json ||= copy_ref: @tag, expires: @expiresAt.toString()
# Creates a CopyReference instance from a raw reference or API response.
#
@@ -74,13 +104,16 @@ class Dropbox.CopyReference
# This constructor is used by Dropbox.CopyReference.parse, and should not be
# called directly.
#
- # @param {?Object, ?String} refData the parsed JSON descring a copy
+ # @param {Object, String} refData the parsed JSON describing a copy
# reference, or the reference string
constructor: (refData) ->
if typeof refData is 'object'
@tag = refData.copy_ref
@expiresAt = new Date Date.parse(refData.expires)
+ @_json = refData
else
@tag = refData
- @expiresAt = new Date()
-
+ @expiresAt = new Date Math.ceil(Date.now() / 1000) * 1000
+ # The JSON representation is created on-demand, to avoid unnecessary
+ # object creation.
+ @_json = null
diff --git a/lib/client/storage/dropbox/src/stat.coffee b/lib/client/storage/dropbox/src/stat.coffee
index 6fe45c44..ea3db318 100644
--- a/lib/client/storage/dropbox/src/stat.coffee
+++ b/lib/client/storage/dropbox/src/stat.coffee
@@ -48,15 +48,15 @@ class Dropbox.Stat
# folder contents twice
versionTag: null
- # @property {String} a guess of the MIME type representing the file or folder's
- # contents
+ # @property {String} a guess of the MIME type representing the file or
+ # folder's contents
mimeType: null
# @property {Number} the size of the file, in bytes; null for folders
size: null
- # @property {String} the size of the file, in a human-readable format, such as
- # "225.4KB"; the format of this string is influenced by the API client's
+ # @property {String} the size of the file, in a human-readable format, such
+ # as "225.4KB"; the format of this string is influenced by the API client's
# locale
humanSize: null
@@ -73,6 +73,13 @@ class Dropbox.Stat
# does not report any time
clientModifiedAt: null
+ # JSON representation of this file / folder's metadata
+ #
+ # @return {Object} conforms to the JSON restrictions; can be passed to
+ # Dropbox.Stat#parse to obtain an identical Stat instance
+ json: ->
+ @_json
+
# Creates a Stat instance from a raw "metadata" response.
#
# @private
@@ -82,6 +89,7 @@ class Dropbox.Stat
# @param {Object} metadata the result of parsing JSON API responses that are
# called "metadata" in the API documentation
constructor: (metadata) ->
+ @_json = metadata
@path = metadata.path
# Ensure there is a trailing /, to make path processing reliable.
@path = '/' + @path if @path.substring(0, 1) isnt '/'
diff --git a/lib/client/storage/dropbox/src/upload_cursor.coffee b/lib/client/storage/dropbox/src/upload_cursor.coffee
new file mode 100644
index 00000000..5e660102
--- /dev/null
+++ b/lib/client/storage/dropbox/src/upload_cursor.coffee
@@ -0,0 +1,59 @@
+# Tracks the progress of a resumable upload.
+class Dropbox.UploadCursor
+ # Creates an UploadCursor instance from an API response.
+ #
+ # @param {?Object, ?String} cursorData the parsed JSON describing the status
+ # of a partial upload, or the upload ID string
+ @parse: (cursorData) ->
+ if cursorData and (typeof cursorData is 'object' or
+ typeof cursorData is 'string')
+ new Dropbox.UploadCursor cursorData
+ else
+ cursorData
+
+ # @property {String} the server-generated ID for this upload
+ tag: null
+
+ # @property {Number} number of bytes that have already been uploaded
+ offset: null
+
+ # @property {Date} deadline for finishing the upload
+ expiresAt: null
+
+ # JSON representation of this cursor.
+ #
+ # @return {Object} conforms to the JSON restrictions; can be passed to
+ # Dropbox.UploadCursor#parse to obtain an identical UploadCursor instance
+ json: ->
+ # NOTE: the assignment only occurs if
+ @_json ||= upload_id: @tag, offset: @offset, expires: @expiresAt.toString()
+
+ # Creates an UploadCursor instance from a raw reference or API response.
+ #
+ # This constructor should only be called directly to obtain a cursor for a
+ # new file upload. Dropbox.UploadCursor#parse should be called instead
+ #
+ # @param {?Object, ?String} cursorData the parsed JSON describing a copy
+ # reference, or the reference string
+ constructor: (cursorData) ->
+ @replace cursorData
+
+ # Replaces the current
+ #
+ # @private Called by Dropbox.Client#resumableUploadStep.
+ #
+ # @param {?Object, ?String} cursorData the parsed JSON describing a copy
+ # reference, or the reference string
+ # @return {Dropbox.UploadCursor} this
+ replace: (cursorData) ->
+ if typeof cursorData is 'object'
+ @tag = cursorData.upload_id or null
+ @offset = cursorData.offset or 0
+ @expiresAt = new Date(Date.parse(cursorData.expires) or Date.now())
+ @_json = cursorData
+ else
+ @tag = cursorData or null
+ @offset = 0
+ @expiresAt = new Date Math.floor(Date.now() / 1000) * 1000
+ @_json = null
+ @
diff --git a/lib/client/storage/dropbox/src/user_info.coffee b/lib/client/storage/dropbox/src/user_info.coffee
index fa61f212..ba6dbea2 100644
--- a/lib/client/storage/dropbox/src/user_info.coffee
+++ b/lib/client/storage/dropbox/src/user_info.coffee
@@ -27,7 +27,8 @@ class Dropbox.UserInfo
# returned by the authentication process
uid: null
- # @property {String}
+ # @property {String} the user's referral link; the user might benefit if
+ # others use the link to sign up for Dropbox
referralUrl: null
# Specific to applications whose access type is "public app folder".
@@ -49,15 +50,23 @@ class Dropbox.UserInfo
# shared with other users
sharedBytes: null
+ # JSON representation of this user's information.
+ #
+ # @return {Object} conforms to the JSON restrictions; can be passed to
+ # Dropbox.UserInfo#parse to obtain an identical UserInfo instance
+ json: ->
+ @_json
+
# Creates a UserInfo instance from a raw API response.
#
# @private
# This constructor is used by Dropbox.UserInfo.parse, and should not be
# called directly.
#
- # @param {?Object} userInfo the result of parsing a JSON API response that
+ # @param {Object} userInfo the result of parsing a JSON API response that
# describes a user
constructor: (userInfo) ->
+ @_json = userInfo
@name = userInfo.display_name
@email = userInfo.email
@countryCode = userInfo.country or null
diff --git a/lib/client/storage/dropbox/src/xhr.coffee b/lib/client/storage/dropbox/src/xhr.coffee
index ef630f67..63051828 100644
--- a/lib/client/storage/dropbox/src/xhr.coffee
+++ b/lib/client/storage/dropbox/src/xhr.coffee
@@ -30,28 +30,41 @@ else
# https://code.google.com/p/chromium/issues/detail?id=60449
if typeof Uint8Array is 'undefined'
DropboxXhrArrayBufferView = null
+ DropboxXhrSendArrayBufferView = false
else
- DropboxXhrArrayBufferView =
- (new Uint8Array(0)).__proto__.__proto__.constructor
+ if Object.getPrototypeOf
+ DropboxXhrArrayBufferView = Object.getPrototypeOf(
+ Object.getPrototypeOf(new Uint8Array(0))).constructor
+ else if Object.__proto__
+ DropboxXhrArrayBufferView =
+ (new Uint8Array(0)).__proto__.__proto__.constructor
+
+ # Browsers that haven't implemented XHR#send(ArrayBufferView) also don't
+ # have a real ArrayBufferView prototype. (Safari, Firefox)
+ DropboxXhrSendArrayBufferView = DropboxXhrArrayBufferView isnt Object
# Dispatches low-level AJAX calls (XMLHttpRequests).
class Dropbox.Xhr
# The object used to perform AJAX requests (XMLHttpRequest).
@Request = DropboxXhrRequest
# Set to true when using the XDomainRequest API.
- @ieMode = DropboxXhrIeMode
+ @ieXdr = DropboxXhrIeMode
# Set to true if the platform has proper support for FormData.
@canSendForms = DropboxXhrCanSendForms
# Set to true if the platform performs CORS preflight checks.
@doesPreflight = DropboxXhrDoesPreflight
+ # Superclass for all ArrayBufferView objects.
@ArrayBufferView = DropboxXhrArrayBufferView
+ # Set to true if we think we can send ArrayBufferView objects via XHR.
+ @sendArrayBufferView = DropboxXhrSendArrayBufferView
+
# Sets up an AJAX request.
#
# @param {String} method the HTTP method used to make the request ('GET',
- # 'POST', 'PUT', etc.)
+ # 'POST', 'PUT', etc.)
# @param {String} baseUrl the URL that receives the request; this URL might
- # be modified, e.g. by appending parameters for GET requests
+ # be modified, e.g. by appending parameters for GET requests
constructor: (@method, baseUrl) ->
@isGet = @method is 'GET'
@url = baseUrl
@@ -63,6 +76,17 @@ class Dropbox.Xhr
@responseType = null
@callback = null
@xhr = null
+ @onError = null
+
+ # @property {?XMLHttpRequest} the raw XMLHttpRequest object used to make the
+ # request; null until Dropbox.Xhr#prepare is called
+ xhr: null
+
+ # @property {?Dropbox.EventSource} if the XHR fails and
+ # this property is set, the Dropbox.ApiError instance that will be passed
+ # to the callback will be dispatched through the Dropbox.EventSource; the
+ # EventSource should be configured for non-cancelable events
+ onError: null
# Sets the parameters (form field values) that will be sent with the request.
#
@@ -100,11 +124,22 @@ class Dropbox.Xhr
# headers) might result in a valid signature that is applied in a sub-optimal
# fashion. For best results, call this right before Dropbox.Xhr#prepare.
#
- signWithOauth: (oauth) ->
- if Dropbox.Xhr.ieMode or (Dropbox.Xhr.doesPreflight and (not @preflight))
+ # @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be
+ # used to sign the request
+ # @param {Boolean} cacheFriendly if true, the signing process choice will be
+ # biased towards allowing the HTTP cache to work; by default, the choice
+ # attempts to avoid the CORS preflight request whenever possible
+ # @return {Dropbox.Xhr} this, for easy call chaining
+ signWithOauth: (oauth, cacheFriendly) ->
+ if Dropbox.Xhr.ieXdr
@addOauthParams oauth
- else
+ else if @preflight or !Dropbox.Xhr.doesPreflight
@addOauthHeader oauth
+ else
+ if @isGet and cacheFriendly
+ @addOauthHeader oauth
+ else
+ @addOauthParams oauth
# Ammends the request parameters to include an OAuth signature.
#
@@ -112,7 +147,7 @@ class Dropbox.Xhr
# the signing process.
#
# @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be
- # used to sign the request
+ # used to sign the request
# @return {Dropbox.Xhr} this, for easy call chaining
addOauthParams: (oauth) ->
if @signed
@@ -129,7 +164,7 @@ class Dropbox.Xhr
# the signing process.
#
# @param {Dropbox.Oauth} oauth OAuth instance whose key and secret will be
- # used to sign the request
+ # used to sign the request
# @return {Dropbox.Xhr} this, for easy call chaining
addOauthHeader: (oauth) ->
if @signed
@@ -142,7 +177,7 @@ class Dropbox.Xhr
# Sets the body (piece of data) that will be sent with the request.
#
# @param {String, Blob, ArrayBuffer} body the body to be sent in a request;
- # GET requests cannot have a body
+ # GET requests cannot have a body
# @return {Dropbox.Xhr} this, for easy call chaining
setBody: (body) ->
if @isGet
@@ -150,9 +185,13 @@ class Dropbox.Xhr
if @body isnt null
throw new Error 'Request already has a body'
- unless @preflight
- unless (typeof FormData isnt 'undefined') and (body instanceof FormData)
- @preflight = true
+ if typeof body is 'string'
+ # Content-Type will be set automatically.
+ else if (typeof FormData isnt 'undefined') and (body instanceof FormData)
+ # Content-Type will be set automatically.
+ else
+ @headers['Content-Type'] = 'application/octet-stream'
+ @preflight = true
@body = body
@
@@ -191,26 +230,37 @@ class Dropbox.Xhr
# Simulates having an being sent with the request.
#
# @param {String} fieldName the name of the form field / parameter (not of
- # the uploaded file)
+ # the uploaded file)
# @param {String} fileName the name of the uploaded file (not the name of the
- # form field / parameter)
+ # form field / parameter)
# @param {String, Blob, File} fileData contents of the file to be uploaded
# @param {?String} contentType the MIME type of the file to be uploaded; if
- # fileData is a Blob or File, its MIME type is used instead
+ # fileData is a Blob or File, its MIME type is used instead
setFileField: (fieldName, fileName, fileData, contentType) ->
if @body isnt null
throw new Error 'Request already has a body'
if @isGet
- throw new Error 'paramsToBody cannot be called on GET requests'
+ throw new Error 'setFileField cannot be called on GET requests'
if typeof(fileData) is 'object' and typeof Blob isnt 'undefined'
- if ArrayBuffer? and fileData instanceof ArrayBuffer
- fileData = new Uint8Array fileData
- if Dropbox.Xhr.ArrayBufferView and
- fileData instanceof Dropbox.Xhr.ArrayBufferView
- contentType or= 'application/octet-stream'
- fileData = new Blob [fileData], type: contentType
+ if typeof ArrayBuffer isnt 'undefined'
+ if fileData instanceof ArrayBuffer
+ # Convert ArrayBuffer -> ArrayBufferView on standard-compliant
+ # browsers, to avoid warnings from the Blob constructor.
+ if Dropbox.Xhr.sendArrayBufferView
+ fileData = new Uint8Array fileData
+ else
+ # Convert ArrayBufferView -> ArrayBuffer on older browsers, to avoid
+ # having a Blob that contains "[object Uint8Array]" instead of the
+ # actual data.
+ if !Dropbox.Xhr.sendArrayBufferView and fileData.byteOffset is 0 and
+ fileData.buffer instanceof ArrayBuffer
+ fileData = fileData.buffer
+
+ contentType or= 'application/octet-stream'
+ fileData = new Blob [fileData], type: contentType
+
# Workaround for http://crbug.com/165095
if typeof File isnt 'undefined' and fileData instanceof File
fileData = new Blob [fileData], type: fileData.type
@@ -236,7 +286,7 @@ class Dropbox.Xhr
# @private
# @return {String} a nonce suitable for use as a part boundary in a multipart
- # MIME message
+ # MIME message
multipartBoundary: ->
[Date.now().toString(36), Math.random().toString(36)].join '----'
@@ -276,8 +326,8 @@ class Dropbox.Xhr
#
# @return {Dropbox.Xhr} this, for easy call chaining
prepare: ->
- ieMode = Dropbox.Xhr.ieMode
- if @isGet or @body isnt null or ieMode
+ ieXdr = Dropbox.Xhr.ieXdr
+ if @isGet or @body isnt null or ieXdr
@paramsToUrl()
if @body isnt null and typeof @body is 'string'
@headers['Content-Type'] = 'text/plain; charset=utf8'
@@ -285,14 +335,18 @@ class Dropbox.Xhr
@paramsToBody()
@xhr = new Dropbox.Xhr.Request()
- if ieMode
- @xhr.onload = => @onLoad()
- @xhr.onerror = => @onError()
+ if ieXdr
+ @xhr.onload = => @onXdrLoad()
+ @xhr.onerror = => @onXdrError()
+ @xhr.ontimeout = => @onXdrError()
+ # NOTE: there are reports that XHR somtimes fails if onprogress doesn't
+ # have any handler
+ @xhr.onprogress = ->
else
@xhr.onreadystatechange = => @onReadyStateChange()
@xhr.open @method, @url, true
- unless ieMode
+ unless ieXdr
for own header, value of @headers
@xhr.setRequestHeader header, value
@@ -321,16 +375,15 @@ class Dropbox.Xhr
if @body isnt null
body = @body
# send() in XHR doesn't like naked ArrayBuffers
- if Dropbox.Xhr.ArrayBufferView and body instanceof ArrayBuffer
+ if Dropbox.Xhr.sendArrayBufferView and body instanceof ArrayBuffer
body = new Uint8Array body
try
@xhr.send body
catch e
- # Firefox doesn't support sending ArrayBufferViews.
# Node.js doesn't implement Blob.
- if typeof Blob isnt 'undefined' and Dropbox.Xhr.ArrayBufferView and
- body instanceof Dropbox.Xhr.ArrayBufferView
+ if !Dropbox.Xhr.sendArrayBufferView and typeof Blob isnt 'undefined'
+ # Firefox doesn't support sending ArrayBufferViews.
body = new Blob [body], type: 'application/octet-stream'
@xhr.send body
else
@@ -381,6 +434,7 @@ class Dropbox.Xhr
if @xhr.status < 200 or @xhr.status >= 300
apiError = new Dropbox.ApiError @xhr, @method, @url
+ @onError.dispatch apiError if @onError
@callback apiError
return true
@@ -432,7 +486,7 @@ class Dropbox.Xhr
true
# Handles the XDomainRequest onload event. (IE 8, 9)
- onLoad: ->
+ onXdrLoad: ->
text = @xhr.responseText
switch @xhr.contentType
when 'application/x-www-form-urlencoded'
@@ -444,7 +498,8 @@ class Dropbox.Xhr
true
# Handles the XDomainRequest onload event. (IE 8, 9)
- onError: ->
+ onXdrError: ->
apiError = new Dropbox.ApiError @xhr, @method, @url
+ @onError.dispatch apiError if @onError
@callback apiError
return true
diff --git a/lib/client/storage/dropbox/test/app_icon/hazard.svg b/lib/client/storage/dropbox/test/app_icon/hazard.svg
new file mode 100644
index 00000000..1c9542c1
--- /dev/null
+++ b/lib/client/storage/dropbox/test/app_icon/hazard.svg
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/client/storage/dropbox/test/app_icon/hazard128.png b/lib/client/storage/dropbox/test/app_icon/hazard128.png
new file mode 100644
index 00000000..92e78693
Binary files /dev/null and b/lib/client/storage/dropbox/test/app_icon/hazard128.png differ
diff --git a/lib/client/storage/dropbox/test/app_icon/hazard16.png b/lib/client/storage/dropbox/test/app_icon/hazard16.png
new file mode 100644
index 00000000..847d67dc
Binary files /dev/null and b/lib/client/storage/dropbox/test/app_icon/hazard16.png differ
diff --git a/lib/client/storage/dropbox/test/app_icon/hazard64.png b/lib/client/storage/dropbox/test/app_icon/hazard64.png
new file mode 100644
index 00000000..afdd91e7
Binary files /dev/null and b/lib/client/storage/dropbox/test/app_icon/hazard64.png differ
diff --git a/lib/client/storage/dropbox/test/app_icon/radiation.svg b/lib/client/storage/dropbox/test/app_icon/radiation.svg
new file mode 100644
index 00000000..f21963e2
--- /dev/null
+++ b/lib/client/storage/dropbox/test/app_icon/radiation.svg
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/client/storage/dropbox/test/app_icon/radiation128.png b/lib/client/storage/dropbox/test/app_icon/radiation128.png
new file mode 100644
index 00000000..eefcc148
Binary files /dev/null and b/lib/client/storage/dropbox/test/app_icon/radiation128.png differ
diff --git a/lib/client/storage/dropbox/test/app_icon/radiation16.png b/lib/client/storage/dropbox/test/app_icon/radiation16.png
new file mode 100644
index 00000000..4626e76c
Binary files /dev/null and b/lib/client/storage/dropbox/test/app_icon/radiation16.png differ
diff --git a/lib/client/storage/dropbox/test/app_icon/radiation64.png b/lib/client/storage/dropbox/test/app_icon/radiation64.png
new file mode 100644
index 00000000..8abe6bc4
Binary files /dev/null and b/lib/client/storage/dropbox/test/app_icon/radiation64.png differ
diff --git a/lib/client/storage/dropbox/test/binary/dropbox.png b/lib/client/storage/dropbox/test/binary/dropbox.png
new file mode 100644
index 00000000..1442d90c
Binary files /dev/null and b/lib/client/storage/dropbox/test/binary/dropbox.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_app/images/icon.svg b/lib/client/storage/dropbox/test/chrome_app/images/icon.svg
new file mode 100644
index 00000000..b074aab1
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_app/images/icon.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/lib/client/storage/dropbox/test/chrome_app/images/icon128.png b/lib/client/storage/dropbox/test/chrome_app/images/icon128.png
new file mode 100644
index 00000000..82d61dbc
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_app/images/icon128.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_app/images/icon16.png b/lib/client/storage/dropbox/test/chrome_app/images/icon16.png
new file mode 100644
index 00000000..86c7e8b7
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_app/images/icon16.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_app/images/icon48.png b/lib/client/storage/dropbox/test/chrome_app/images/icon48.png
new file mode 100644
index 00000000..529cf14f
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_app/images/icon48.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_app/manifests/app_v1.json b/lib/client/storage/dropbox/test/chrome_app/manifests/app_v1.json
new file mode 100644
index 00000000..5e369ae0
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_app/manifests/app_v1.json
@@ -0,0 +1,20 @@
+{
+ "name": "dropbox.js Test Suite",
+ "version": "1.0",
+ "manifest_version": 2,
+ "description": "Test suite for Chrome applications and extensions.",
+ "icons": {
+ "16": "images/icon16.png",
+ "48": "images/icon48.png",
+ "128": "images/icon128.png"
+ },
+ "permissions": [
+ "storage",
+ "unlimitedStorage"
+ ],
+ "app": {
+ "launch": {
+ "local_path": "test/html/browser_test.html"
+ }
+ }
+}
diff --git a/lib/client/storage/dropbox/test/chrome_app/manifests/app_v2.json b/lib/client/storage/dropbox/test/chrome_app/manifests/app_v2.json
new file mode 100644
index 00000000..a41b4611
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_app/manifests/app_v2.json
@@ -0,0 +1,22 @@
+{
+ "name": "dropbox.js Test Suite",
+ "version": "1.0",
+ "manifest_version": 2,
+ "description": "Test suite for Chrome applications and extensions.",
+ "icons": {
+ "16": "images/icon16.png",
+ "48": "images/icon48.png",
+ "128": "images/icon128.png"
+ },
+ "permissions": [
+ "storage",
+ "unlimitedStorage"
+ ],
+ "app": {
+ "background": {
+ "scripts": [
+ "test/js/chrome_app_background.js"
+ ]
+ }
+ }
+}
diff --git a/lib/client/storage/dropbox/test/chrome_extension/README.md b/lib/client/storage/dropbox/test/chrome_extension/README.md
new file mode 100644
index 00000000..34c15033
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_extension/README.md
@@ -0,0 +1,10 @@
+# dropbox.js Test Automator
+
+This is a Google Chrome extension that fully automates the dropbox.js test
+suite. Read the
+[dropbox.js development guide](https://github.com/dropbox/dropbox-js/tree/master/doc)
+to learn how the extension fits into the testing process.
+
+You're welcome to reuse the code to automate the testing of your own
+application's integration with Dropbox.
+
diff --git a/lib/client/storage/dropbox/test/chrome_extension/background.coffee b/lib/client/storage/dropbox/test/chrome_extension/background.coffee
new file mode 100644
index 00000000..b29393ba
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_extension/background.coffee
@@ -0,0 +1,84 @@
+# Background script orchestrating the dropbox.js testing automation.
+
+class Automator
+ constructor: ->
+ @wired = false
+ chrome.storage.sync.get 'enabled', (values) =>
+ @lifeSwitch values.enabled is 'true'
+
+ # Activates or deactivates the extension.
+ # @param {Boolean} enabled if true, the extension's functionality is enabled
+ lifeSwitch: (enabled) ->
+ if enabled
+ chrome.browserAction.setIcon
+ path:
+ 19: 'images/action_on19.png'
+ 38: 'images/action_on38.png'
+ chrome.browserAction.setTitle title: '(on) dropbox.js Test Automator'
+ @wire()
+ else
+ chrome.browserAction.setIcon
+ path:
+ 19: 'images/action_off19.png',
+ 38: 'images/action_off38.png'
+ chrome.browserAction.setTitle title: '(off) dropbox.js Test Automator'
+ @unwire()
+
+ # Checks if Dropbox's authorization dialog should be auto-clicked.
+ # @param {String} url the URL of the Dropbox authorization dialog
+ # @return {Boolean} true if the "Authorize" button should be auto-clicked
+ shouldAutomateAuth: (url) ->
+ return false unless @wired
+ !!(/(\?|&)oauth_callback=https?%3A%2F%2Flocalhost%3A891[12]%2F/.exec(url))
+
+ # Checks if an OAuth receiver window should be auto-closed.
+ # @param {String} url the URL of the OAuth receiver window
+ # @return {Boolean} true if the "Authorize" button should be auto-clicked
+ shouldAutomateClose: (url) ->
+ return false unless @wired
+ !!(/^https?:\/\/localhost:8912\/oauth_callback\?/.exec(url))
+
+ # Sets up all the features that make dropbox.js testing easier.
+ wire: ->
+ return if @wired
+ chrome.contentSettings.popups.set(
+ primaryPattern: 'http://localhost:8911/*', setting: 'allow')
+ @wired = true
+ @
+
+ # Disables the features that automate dropbox.js testing.
+ unwire: ->
+ return unless @wired
+ chrome.contentSettings.popups.clear({})
+ @wired = false
+ @
+
+# Global Automator instance.
+automator = new Automator()
+
+# Current extension id, used to validate incoming messages.
+extensionId = chrome.i18n.getMessage "@@extension_id"
+
+# Communicates with content scripts.
+chrome.extension.onMessage.addListener (message, sender, sendResponse) ->
+ return unless sender.id is extensionId
+ switch message.type
+ when 'auth'
+ sendResponse automate: automator.shouldAutomateAuth(message.url)
+ when 'close'
+ if automator.shouldAutomateClose(message.url) and sender.tab
+ chrome.tabs.remove sender.tab.id
+
+# Listen to pref changes and activate / deactivate the extension.
+chrome.storage.onChanged.addListener (changes, namespace) ->
+ return unless namespace is 'sync'
+ for name, change of changes
+ continue unless name is 'enabled'
+ automator.lifeSwitch change.newValue is 'true'
+
+# The browser action item flips the switch that activates the extension.
+chrome.browserAction.onClicked.addListener ->
+ chrome.storage.sync.get 'enabled', (values) ->
+ enabled = values.enabled is 'true'
+ chrome.storage.sync.set enabled: (!enabled).toString()
+
diff --git a/lib/client/storage/dropbox/test/chrome_extension/content_auth.coffee b/lib/client/storage/dropbox/test/chrome_extension/content_auth.coffee
new file mode 100644
index 00000000..1fb6ca89
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_extension/content_auth.coffee
@@ -0,0 +1,18 @@
+# Content script for Dropbox authorization pages.
+
+message = type: 'auth', url: window.location.href
+chrome.extension.sendMessage message, (response) ->
+ return unless response.automate
+
+ button = document.querySelector('[name=allow_access]') or
+ document.querySelector '.freshbutton-blue'
+ event = document.createEvent 'MouseEvents'
+
+ clientX = button.clientWidth / 2
+ clientY = button.clientHeight / 2
+ screenX = window.screenX + button.offsetLeft + clientX
+ screenY = window.screenY + button.offsetTop + clientY
+ event.initMouseEvent 'click', true, true, window, 1,
+ screenX, screenY, clientX, clientY, false, false, false, false, 0, null
+ button.dispatchEvent event
+
diff --git a/lib/client/storage/dropbox/test/chrome_extension/content_close.coffee b/lib/client/storage/dropbox/test/chrome_extension/content_close.coffee
new file mode 100644
index 00000000..0ca6febd
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_extension/content_close.coffee
@@ -0,0 +1,4 @@
+# Content script for Dropbox OAuth receiver pages.
+
+message = type: 'close', url: window.location.href
+chrome.extension.sendMessage message
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/action_off19.png b/lib/client/storage/dropbox/test/chrome_extension/images/action_off19.png
new file mode 100644
index 00000000..734c470d
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/action_off19.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/action_off38.png b/lib/client/storage/dropbox/test/chrome_extension/images/action_off38.png
new file mode 100644
index 00000000..c6e96cd1
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/action_off38.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/action_on19.png b/lib/client/storage/dropbox/test/chrome_extension/images/action_on19.png
new file mode 100644
index 00000000..290b36b6
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/action_on19.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/action_on38.png b/lib/client/storage/dropbox/test/chrome_extension/images/action_on38.png
new file mode 100644
index 00000000..86dfb268
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/action_on38.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/icon.svg b/lib/client/storage/dropbox/test/chrome_extension/images/icon.svg
new file mode 100644
index 00000000..4a99e835
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_extension/images/icon.svg
@@ -0,0 +1,69 @@
+
+
+
+image/svg+xml
\ No newline at end of file
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/icon128.png b/lib/client/storage/dropbox/test/chrome_extension/images/icon128.png
new file mode 100644
index 00000000..6a6c2e11
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/icon128.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/icon16.png b/lib/client/storage/dropbox/test/chrome_extension/images/icon16.png
new file mode 100644
index 00000000..3374631a
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/icon16.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/images/icon48.png b/lib/client/storage/dropbox/test/chrome_extension/images/icon48.png
new file mode 100644
index 00000000..360607b2
Binary files /dev/null and b/lib/client/storage/dropbox/test/chrome_extension/images/icon48.png differ
diff --git a/lib/client/storage/dropbox/test/chrome_extension/manifest.json b/lib/client/storage/dropbox/test/chrome_extension/manifest.json
new file mode 100644
index 00000000..f6fe0e57
--- /dev/null
+++ b/lib/client/storage/dropbox/test/chrome_extension/manifest.json
@@ -0,0 +1,44 @@
+{
+ "name": "dropbox.js Test Automator",
+ "version": "1.0",
+ "manifest_version": 2,
+ "description": "Automatically clicks buttons and closes windows, so you can spend more time coding.",
+ "icons": {
+ "16": "images/icon16.png",
+ "48": "images/icon48.png",
+ "128": "images/icon128.png"
+ },
+ "permissions": [
+ "http://localhost/*",
+ "https://localhost/*",
+ "https://www.dropbox.com/1/oauth/authorize*",
+ "contentSettings",
+ "storage"
+ ],
+ "browser_action": {
+ "default_icon": {
+ "19": "images/action_off19.png",
+ "38": "images/action_off38.png"
+ },
+ "default_title": "dropbox.js Test Automator"
+ },
+ "background": {
+ "scripts": ["background.js"]
+ },
+ "content_scripts": [
+ {
+ "matches": ["https://www.dropbox.com/1/oauth/authorize*"],
+ "js": ["content_auth.js"],
+ "run_at": "document_idle"
+ },
+ {
+ "matches": [
+ "http://localhost/*",
+ "https://localhost/*"
+ ],
+ "include_globs": ["http*://localhost:8912/oauth_callback*"],
+ "js": ["content_close.js"],
+ "run_at": "document_idle"
+ }
+ ]
+}
diff --git a/lib/client/storage/dropbox/test/html/browser_test.html b/lib/client/storage/dropbox/test/html/browser_test.html
new file mode 100644
index 00000000..21bb0cec
--- /dev/null
+++ b/lib/client/storage/dropbox/test/html/browser_test.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+ dropbox.js browser tests
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/client/storage/dropbox/test/html/chrome_oauth_receiver.html b/lib/client/storage/dropbox/test/html/chrome_oauth_receiver.html
new file mode 100644
index 00000000..0f0db938
--- /dev/null
+++ b/lib/client/storage/dropbox/test/html/chrome_oauth_receiver.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+ Dropbox sign-in successful
+
+ Please close this window.
+
+
diff --git a/lib/client/storage/dropbox/test/html/oauth_receiver.html b/lib/client/storage/dropbox/test/html/oauth_receiver.html
new file mode 100644
index 00000000..54a70562
--- /dev/null
+++ b/lib/client/storage/dropbox/test/html/oauth_receiver.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ Dropbox sign-in successful
+
+ Please close this window.
+
+
diff --git a/lib/client/storage/dropbox/test/html/redirect_driver_test.html b/lib/client/storage/dropbox/test/html/redirect_driver_test.html
new file mode 100644
index 00000000..8e5e2410
--- /dev/null
+++ b/lib/client/storage/dropbox/test/html/redirect_driver_test.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+ Please close this window.
+
+
diff --git a/lib/client/storage/dropbox/test/src/base64_test.coffee b/lib/client/storage/dropbox/test/src/base64_test.coffee
new file mode 100644
index 00000000..975169f2
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/base64_test.coffee
@@ -0,0 +1,11 @@
+describe 'Dropbox.atob', ->
+ it 'decodes an ASCII string', ->
+ expect(Dropbox.atob('YTFiMmMz')).to.equal 'a1b2c3'
+ it 'decodes a non-ASCII character', ->
+ expect(Dropbox.atob('/A==')).to.equal String.fromCharCode(252)
+
+describe 'Dropbox.btoa', ->
+ it 'encodes an ASCII string', ->
+ expect(Dropbox.btoa('a1b2c3')).to.equal 'YTFiMmMz'
+ it 'encodes a non-ASCII character', ->
+ expect(Dropbox.btoa(String.fromCharCode(252))).to.equal '/A=='
diff --git a/lib/client/storage/dropbox/test/src/browser_mocha_runner.coffee b/lib/client/storage/dropbox/test/src/browser_mocha_runner.coffee
new file mode 100644
index 00000000..e538dce8
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/browser_mocha_runner.coffee
@@ -0,0 +1,9 @@
+window.addEventListener 'load', ->
+ runner = mocha.run ->
+ failures = runner.failures || 0
+ total = runner.total || 0
+ image = new Image()
+ image.src = "/diediedie?failed=#{failures}&total=#{total}";
+ image.onload = ->
+ null
+
diff --git a/lib/client/storage/dropbox/test/src/browser_mocha_setup.coffee b/lib/client/storage/dropbox/test/src/browser_mocha_setup.coffee
new file mode 100644
index 00000000..70a2f3b7
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/browser_mocha_setup.coffee
@@ -0,0 +1 @@
+mocha.setup ui: 'bdd', slow: 150, timeout: 10000
diff --git a/lib/client/storage/dropbox/test/src/chrome_app_background.coffee b/lib/client/storage/dropbox/test/src/chrome_app_background.coffee
new file mode 100644
index 00000000..634d0199
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/chrome_app_background.coffee
@@ -0,0 +1,3 @@
+chrome.app.runtime.onLaunched.addListener ->
+ chrome.app.window.create 'test/html/browser_test.html',
+ type: 'shell', frame: 'chrome', id: 'test_suite'
diff --git a/lib/client/storage/dropbox/test/src/chrome_oauth_receiver.coffee b/lib/client/storage/dropbox/test/src/chrome_oauth_receiver.coffee
new file mode 100644
index 00000000..de601b08
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/chrome_oauth_receiver.coffee
@@ -0,0 +1 @@
+Dropbox.Drivers.Chrome.oauthReceiver()
diff --git a/lib/client/storage/dropbox/test/src/client_test.coffee b/lib/client/storage/dropbox/test/src/client_test.coffee
new file mode 100644
index 00000000..47d57185
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/client_test.coffee
@@ -0,0 +1,1492 @@
+buildClientTests = (clientKeys) ->
+ # Creates the global client.
+ setupClient = (test, done) ->
+ # Should only be used for fixture teardown.
+ test.__client = new Dropbox.Client clientKeys
+ done()
+
+ # Creates the test directory.
+ setupDirectory = (test, done) ->
+ # True if running on node.js
+ test.node_js = module? and module?.exports? and require?
+
+ # All test data should go here.
+ test.testFolder = '/js tests.' + Math.random().toString(36)
+ test.__client.mkdir test.testFolder, (error, stat) ->
+ expect(error).to.equal null
+ done()
+
+ # Creates the binary image file in the test directory.
+ setupImageFile = (test, done) ->
+ test.imageFile = "#{test.testFolder}/test-binary-image.png"
+ test.imageFileData = testImageBytes
+
+ setupImageFileUsingArrayBuffer test, (success) ->
+ if success
+ return done()
+ setupImageFileUsingBlob test, (success) ->
+ if success
+ return done()
+ setupImageFileUsingString test, done
+
+ # Standard-compliant browsers write via XHR#send(ArrayBufferView).
+ setupImageFileUsingArrayBuffer = (test, done) ->
+ if Uint8Array? and (not test.node_js)
+ testImageServerOn()
+ xhr = new Dropbox.Xhr 'GET', testImageUrl
+ xhr.setResponseType('arraybuffer').prepare().send (error, buffer) =>
+ testImageServerOff()
+ expect(error).to.equal null
+ test.__client.writeFile test.imageFile, buffer, (error, stat) ->
+ expect(error).to.equal null
+ # Some browsers will send the '[object Uint8Array]' string instead of
+ # the ArrayBufferView.
+ if stat.size is buffer.byteLength
+ test.imageFileTag = stat.versionTag
+ done true
+ else
+ done false
+ else
+ done false
+
+ # Fallback to XHR#send(Blob).
+ setupImageFileUsingBlob = (test, done) ->
+ if Blob? and (not test.node_js)
+ testImageServerOn()
+ xhr = new Dropbox.Xhr 'GET', testImageUrl
+ xhr.setResponseType('arraybuffer').prepare().send (error, buffer) =>
+ testImageServerOff()
+ expect(error).to.equal null
+ blob = new Blob [buffer], type: 'image/png'
+ test.__client.writeFile test.imageFile, blob, (error, stat) ->
+ expect(error).to.equal null
+ if stat.size is blob.size
+ test.imageFileTag = stat.versionTag
+ done true
+ else
+ done false
+ else
+ done false
+
+ # Last resort: send a string that will get crushed by encoding errors.
+ setupImageFileUsingString = (test, done) ->
+ test.__client.writeFile(test.imageFile, test.imageFileData,
+ { binary: true },
+ (error, stat) ->
+ expect(error).to.equal null
+ test.imageFileTag = stat.versionTag
+ done()
+ )
+
+ # Creates the plaintext file in the test directory.
+ setupTextFile = (test, done) ->
+ test.textFile = "#{test.testFolder}/test-file.txt"
+ test.textFileData = "Plaintext test file #{Math.random().toString(36)}.\n"
+ test.__client.writeFile(test.textFile, test.textFileData,
+ (error, stat) ->
+ expect(error).to.equal null
+ test.textFileTag = stat.versionTag
+ done()
+ )
+
+ # Global (expensive) fixtures.
+ before (done) ->
+ @timeout 10 * 1000
+ setupClient this, =>
+ setupDirectory this, =>
+ setupImageFile this, =>
+ setupTextFile this, ->
+ done()
+
+ # Teardown for global fixtures.
+ after (done) ->
+ @__client.remove @testFolder, (error, stat) =>
+ @test.error(new Error(error)) if error
+ done()
+
+ # Per-test (cheap) fixtures.
+ beforeEach ->
+ @client = new Dropbox.Client clientKeys
+
+ describe 'URLs for custom API server', ->
+ it 'computes the other URLs correctly', ->
+ client = new Dropbox.Client
+ key: clientKeys.key,
+ secret: clientKeys.secret,
+ server: 'https://api.sandbox.dropbox-proxy.com'
+
+ expect(client.apiServer).to.equal(
+ 'https://api.sandbox.dropbox-proxy.com')
+ expect(client.authServer).to.equal(
+ 'https://www.sandbox.dropbox-proxy.com')
+ expect(client.fileServer).to.equal(
+ 'https://api-content.sandbox.dropbox-proxy.com')
+
+ describe '#normalizePath', ->
+ it "doesn't touch relative paths", ->
+ expect(@client.normalizePath('aa/b/cc/dd')).to.equal 'aa/b/cc/dd'
+
+ it 'removes the leading / from absolute paths', ->
+ expect(@client.normalizePath('/aaa/b/cc/dd')).to.equal 'aaa/b/cc/dd'
+
+ it 'removes multiple leading /s from absolute paths', ->
+ expect(@client.normalizePath('///aa/b/ccc/dd')).to.equal 'aa/b/ccc/dd'
+
+ describe '#urlEncodePath', ->
+ it 'encodes each segment separately', ->
+ expect(@client.urlEncodePath('a b+c/d?e"f/g&h')).to.
+ equal "a%20b%2Bc/d%3Fe%22f/g%26h"
+ it 'normalizes paths', ->
+ expect(@client.urlEncodePath('///a b+c/g&h')).to.
+ equal "a%20b%2Bc/g%26h"
+
+ describe '#dropboxUid', ->
+ it 'matches the uid in the credentials', ->
+ expect(@client.dropboxUid()).to.equal clientKeys.uid
+
+ describe '#getUserInfo', ->
+ it 'returns reasonable information', (done) ->
+ @client.getUserInfo (error, userInfo, rawUserInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ expect(userInfo.uid).to.equal clientKeys.uid
+ expect(rawUserInfo).not.to.be.instanceOf Dropbox.UserInfo
+ expect(rawUserInfo).to.have.property 'uid'
+ done()
+
+ describe 'with httpCache', ->
+ beforeEach ->
+ @xhr = null
+ @client.onXhr.addListener (xhr) =>
+ @xhr = xhr
+
+ it 'uses Authorization headers', (done) ->
+ @client.getUserInfo httpCache: true, (error, userInfo, rawUserInfo) =>
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers
+ expect(@xhr.url).to.contain 'oauth_nonce'
+ else
+ expect(@xhr.headers).to.have.key 'Authorization'
+
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ expect(userInfo.uid).to.equal clientKeys.uid
+ expect(rawUserInfo).not.to.be.instanceOf Dropbox.UserInfo
+ expect(rawUserInfo).to.have.property 'uid'
+ done()
+
+ describe '#mkdir', ->
+ afterEach (done) ->
+ return done() unless @newFolder
+ @client.remove @newFolder, (error, stat) -> done()
+
+ it 'creates a folder in the test folder', (done) ->
+ @newFolder = "#{@testFolder}/test'folder"
+ @client.mkdir @newFolder, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFolder
+ expect(stat.isFolder).to.equal true
+ @client.stat @newFolder, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat.isFolder).to.equal true
+ done()
+
+ describe '#readFile', ->
+ it 'reads a text file', (done) ->
+ @client.readFile @textFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads the beginning of a text file', (done) ->
+ return done() if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+
+ @client.readFile @textFile, start: 0, length: 10, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData.substring(0, 10)
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads the middle of a text file', (done) ->
+ return done() if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+
+ @client.readFile @textFile, start: 8, length: 10, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData.substring(8, 18)
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads the end of a text file via the start: option', (done) ->
+ return done() if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+
+ @client.readFile @textFile, start: 10, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData.substring(10)
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads the end of a text file via the length: option', (done) ->
+ return done() if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+
+ @client.readFile @textFile, length: 10, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.
+ equal @textFileData.substring(@textFileData.length - 10)
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads a binary file into a string', (done) ->
+ @client.readFile @imageFile, binary: true, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @imageFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @imageFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads a binary file into a Blob', (done) ->
+ return done() unless Blob?
+ @client.readFile @imageFile, blob: true, (error, blob, stat) =>
+ expect(error).to.equal null
+ expect(blob).to.be.instanceOf Blob
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @imageFile
+ expect(stat.isFile).to.equal true
+ reader = new FileReader
+ reader.onloadend = =>
+ return unless reader.readyState == FileReader.DONE
+ buffer = reader.result
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+ reader.readAsArrayBuffer blob
+
+ it 'reads a binary file into an ArrayBuffer', (done) ->
+ return done() unless ArrayBuffer?
+ @client.readFile @imageFile, arrayBuffer: true, (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @imageFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ describe 'with an onXhr listener', ->
+ beforeEach ->
+ @listenerXhr = null
+ @callbackCalled = false
+
+ it 'calls the listener with a Dropbox.Xhr argument', (done) ->
+ @client.onXhr.addListener (xhr) =>
+ expect(xhr).to.be.instanceOf Dropbox.Xhr
+ @listenerXhr = xhr
+ true
+
+ @client.readFile @textFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ done() if @listenerXhr
+
+ it 'calls the listener before firing the XHR', (done) ->
+ @client.onXhr.addListener (xhr) =>
+ unless Dropbox.Xhr.ieXdr # IE's XHR doesn't have readyState
+ expect(xhr.xhr.readyState).to.equal 1
+ expect(@callbackCalled).to.equal false
+ @listenerXhr = xhr
+ true
+
+ @client.readFile @textFile, (error, data, stat) =>
+ @callbackCalled = true
+ expect(@listenerXhr).to.be.instanceOf Dropbox.Xhr
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ done() if @listenerXhr
+
+ it 'does not send the XHR if the listener cancels the event', (done) ->
+ @client.onXhr.addListener (xhr) =>
+ expect(@callbackCalled).to.equal false
+ @listenerXhr = xhr
+ # NOTE: if the client calls send(), a DOM error will fail the test
+ xhr.send()
+ false
+
+ @client.readFile @textFile, (error, data, stat) =>
+ @callbackCalled = true
+ expect(@listenerXhr).to.be.instanceOf Dropbox.Xhr
+ done() if @listenerXhr
+
+ describe 'with httpCache', ->
+ beforeEach ->
+ @xhr = null
+ @client.onXhr.addListener (xhr) =>
+ @xhr = xhr
+
+ it 'reads a text file using Authorization headers', (done) ->
+ @client.readFile @textFile, httpCache: true, (error, data, stat) =>
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers
+ expect(@xhr.url).to.contain 'oauth_nonce'
+ else
+ expect(@xhr.headers).to.have.key 'Authorization'
+
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ describe '#writeFile', ->
+ afterEach (done) ->
+ return done() unless @newFile
+ @client.remove @newFile, (error, stat) -> done()
+
+ it 'writes a new text file', (done) ->
+ @newFile = "#{@testFolder}/another text file.txt"
+ @newFileData = "Another plaintext file #{Math.random().toString(36)}."
+ @client.writeFile @newFile, @newFileData, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @newFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'writes a new empty file', (done) ->
+ @newFile = "#{@testFolder}/another text file.txt"
+ @newFileData = ''
+ @client.writeFile @newFile, @newFileData, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @newFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'writes a Blob to a binary file', (done) ->
+ return done() unless Blob? and ArrayBuffer?
+ @newFile = "#{@testFolder}/test image from blob.png"
+ newBuffer = new ArrayBuffer @imageFileData.length
+ newBytes = new Uint8Array newBuffer
+ for i in [0...@imageFileData.length]
+ newBytes[i] = @imageFileData.charCodeAt i
+ @newBlob = new Blob [newBytes], type: 'image/png'
+ if @newBlob.size isnt newBuffer.byteLength
+ @newBlob = new Blob [newBuffer], type: 'image/png'
+ @client.writeFile @newFile, @newBlob, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ it 'writes a File to a binary file', (done) ->
+ return done() unless File? and Blob? and ArrayBuffer?
+ @newFile = "#{@testFolder}/test image from blob.png"
+ newBuffer = new ArrayBuffer @imageFileData.length
+ newBytes = new Uint8Array newBuffer
+ for i in [0...@imageFileData.length]
+ newBytes[i] = @imageFileData.charCodeAt i
+ newBlob = new Blob [newBytes], type: 'image/png'
+
+ # Called when we have a File wrapping newBlob.
+ actualTestCase = (file) =>
+ @newFileObject = file
+ @client.writeFile @newFile, @newFileObject, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ # TODO(pwnall): use lighter method of constructing a File, when available
+ # http://crbug.com/164933
+ return done() if typeof webkitRequestFileSystem is 'undefined'
+ webkitRequestFileSystem window.TEMPORARY, 1024 * 1024, (fileSystem) ->
+ # NOTE: the File name is different from the uploaded file name, to
+ # catch bugs such as http://crbug.com/165095
+ fileSystem.root.getFile 'test image file.png',
+ create: true, exclusive: false, (fileEntry) ->
+ fileEntry.createWriter (fileWriter) ->
+ fileWriter.onwriteend = ->
+ fileEntry.file (file) ->
+ actualTestCase file
+ fileWriter.write newBlob
+
+ it 'writes an ArrayBuffer to a binary file', (done) ->
+ return done() unless ArrayBuffer?
+ @newFile = "#{@testFolder}/test image from arraybuffer.png"
+ @newBuffer = new ArrayBuffer @imageFileData.length
+ newBytes = new Uint8Array @newBuffer
+ for i in [0...@imageFileData.length]
+ newBytes[i] = @imageFileData.charCodeAt i
+ @client.writeFile @newFile, @newBuffer, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ it 'writes an ArrayBufferView to a binary file', (done) ->
+ return done() unless ArrayBuffer?
+ @newFile = "#{@testFolder}/test image from arraybufferview.png"
+ @newBytes = new Uint8Array @imageFileData.length
+ for i in [0...@imageFileData.length]
+ @newBytes[i] = @imageFileData.charCodeAt i
+ @client.writeFile @newFile, @newBytes, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ describe '#resumableUploadStep + #resumableUploadFinish', ->
+ beforeEach ->
+ if ArrayBuffer? # IE9 and below doesn't have ArrayBuffer
+ @length1 = Math.ceil @imageFileData.length / 3
+ @length2 = @imageFileData.length - @length1
+ @buffer1 = new ArrayBuffer @length1
+
+ @view1 = new Uint8Array @buffer1
+ for i in [0...@length1]
+ @view1[i] = @imageFileData.charCodeAt i
+ @buffer2 = new ArrayBuffer @length2
+ @view2 = new Uint8Array @buffer2
+ for i in [0...@length2]
+ @view2[i] = @imageFileData.charCodeAt @length1 + i
+
+ if Blob? # node.js and IE9 and below don't have Blob
+ @blob1 = new Blob [@view1], type: 'image/png'
+ if @blob1.size isnt @buffer1.byteLength
+ @blob1 = new Blob [@buffer1], type: 'image/png'
+ @blob2 = new Blob [@view2], type: 'image/png'
+ if @blob2.size isnt @buffer2.byteLength
+ @blob2 = new Blob [@buffer2], type: 'image/png'
+
+ afterEach (done) ->
+ @timeout 20 * 1000 # This sequence is slow on the current API server.
+ return done() unless @newFile
+ @client.remove @newFile, (error, stat) -> done()
+
+ it 'writes a text file in two stages', (done) ->
+ @timeout 20 * 1000 # This sequence is slow on the current API server.
+
+ @newFile = "#{@testFolder}/test resumable upload.txt"
+ line1 = "This is the first fragment\n"
+ line2 = "This is the second fragment\n"
+ @client.resumableUploadStep line1, null, (error, cursor1) =>
+ expect(error).to.equal null
+ expect(cursor1).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor1.offset).to.equal line1.length
+ @client.resumableUploadStep line2, cursor1, (error, cursor2) =>
+ expect(error).to.equal null
+ expect(cursor2).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor2.offset).to.equal line1.length + line2.length
+ expect(cursor2.tag).to.equal cursor1.tag
+ @client.resumableUploadFinish @newFile, cursor2, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal line1 + line2
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'writes a binary file using two ArrayBuffers', (done) ->
+ return done() unless @buffer1
+ @timeout 20 * 1000 # This sequence is slow on the current API server.
+
+ @newFile = "#{@testFolder}/test resumable arraybuffer upload.png"
+ @client.resumableUploadStep @buffer1, null, (error, cursor1) =>
+ expect(error).to.equal null
+ expect(cursor1).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor1.offset).to.equal @length1
+ @client.resumableUploadStep @buffer2, cursor1, (error, cursor2) =>
+ expect(error).to.equal null
+ expect(cursor2).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor2.offset).to.equal @length1 + @length2
+ expect(cursor2.tag).to.equal cursor1.tag
+ @client.resumableUploadFinish @newFile, cursor2, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ it 'writes a binary file using two ArrayBufferViews', (done) ->
+ return done() unless @view1
+ @timeout 20 * 1000 # This sequence is slow on the current API server.
+
+ @newFile = "#{@testFolder}/test resumable arraybuffer upload.png"
+ @client.resumableUploadStep @buffer1, null, (error, cursor1) =>
+ expect(error).to.equal null
+ expect(cursor1).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor1.offset).to.equal @length1
+ @client.resumableUploadStep @buffer2, cursor1, (error, cursor2) =>
+ expect(error).to.equal null
+ expect(cursor2).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor2.offset).to.equal @length1 + @length2
+ expect(cursor2.tag).to.equal cursor1.tag
+ @client.resumableUploadFinish @newFile, cursor2, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+
+ it 'writes a binary file using two Blobs', (done) ->
+ return done() unless @blob1
+ @timeout 20 * 1000 # This sequence is slow on the current API server.
+
+ @newFile = "#{@testFolder}/test resumable blob upload.png"
+ @client.resumableUploadStep @blob1, null, (error, cursor1) =>
+ expect(error).to.equal null
+ expect(cursor1).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor1.offset).to.equal @length1
+ @client.resumableUploadStep @blob2, cursor1, (error, cursor2) =>
+ expect(error).to.equal null
+ expect(cursor2).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor2.offset).to.equal @length1 + @length2
+ expect(cursor2.tag).to.equal cursor1.tag
+ @client.resumableUploadFinish @newFile, cursor2, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, arrayBuffer: true,
+ (error, buffer, stat) =>
+ expect(error).to.equal null
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal @imageFileData
+ done()
+
+ it 'recovers from out-of-sync correctly', (done) ->
+ # IE's XDR doesn't return anything on errors, so we can't do recovery.
+ return done() if Dropbox.Xhr.ieXdr
+ @timeout 20 * 1000 # This sequence is slow on the current API server.
+
+ @newFile = "#{@testFolder}/test resumable upload out of sync.txt"
+ line1 = "This is the first fragment\n"
+ line2 = "This is the second fragment\n"
+ @client.resumableUploadStep line1, null, (error, cursor1) =>
+ expect(error).to.equal null
+ expect(cursor1).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor1.offset).to.equal line1.length
+ cursor1.offset += 10
+ @client.resumableUploadStep line2, cursor1, (error, cursor2) =>
+ expect(error).to.equal null
+ expect(cursor2).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor2.offset).to.equal line1.length
+ expect(cursor2.tag).to.equal cursor1.tag
+ @client.resumableUploadStep line2, cursor2, (error, cursor3) =>
+ expect(error).to.equal null
+ expect(cursor3).to.be.instanceOf Dropbox.UploadCursor
+ expect(cursor3.offset).to.equal line1.length + line2.length
+ expect(cursor3.tag).to.equal cursor1.tag
+ @client.resumableUploadFinish @newFile, cursor3, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal line1 + line2
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reports errors correctly', (done) ->
+ @newFile = "#{@testFolder}/test resumable upload error.txt"
+ badCursor = new Dropbox.UploadCursor 'trollcursor'
+ badCursor.offset = 42
+ @client.resumableUploadStep @textFileData, badCursor, (error, cursor) =>
+ expect(cursor).to.equal undefined
+ expect(error).to.be.instanceOf Dropbox.ApiError
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do status codes.
+ expect(error.status).to.equal 404
+ done()
+
+ describe '#stat', ->
+ it 'retrieves a Stat for a file', (done) ->
+ @client.stat @textFile, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ expect(stat.versionTag).to.equal @textFileTag
+ expect(stat.size).to.equal @textFileData.length
+ if clientKeys.sandbox
+ expect(stat.inAppFolder).to.equal true
+ else
+ expect(stat.inAppFolder).to.equal false
+ done()
+
+ it 'retrieves a Stat for a folder', (done) ->
+ @client.stat @testFolder, (error, stat, entries) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @testFolder
+ expect(stat.isFolder).to.equal true
+ expect(stat.size).to.equal 0
+ if clientKeys.sandbox
+ expect(stat.inAppFolder).to.equal true
+ else
+ expect(stat.inAppFolder).to.equal false
+ expect(entries).to.equal undefined
+ done()
+
+ it 'retrieves a Stat and entries for a folder', (done) ->
+ @client.stat @testFolder, { readDir: true }, (error, stat, entries) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @testFolder
+ expect(stat.isFolder).to.equal true
+ expect(entries).to.be.ok
+ expect(entries).to.have.length 2
+ expect(entries[0]).to.be.instanceOf Dropbox.Stat
+ expect(entries[0].path).not.to.equal @testFolder
+ expect(entries[0].path).to.have.string @testFolder
+ done()
+
+ it 'fails cleanly for a non-existing path', (done) ->
+ listenerError = null
+ @client.onError.addListener (error) -> listenerError = error
+
+ @client.stat @testFolder + '/should_404.txt', (error, stat, entries) =>
+ expect(stat).to.equal undefined
+ expect(entries).to.equal.null
+ expect(error).to.be.instanceOf Dropbox.ApiError
+ expect(listenerError).to.equal error
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do status codes.
+ expect(error).to.have.property 'status'
+ expect(error.status).to.equal 404
+ done()
+
+ describe 'with httpCache', ->
+ beforeEach ->
+ @xhr = null
+ @client.onXhr.addListener (xhr) =>
+ @xhr = xhr
+
+ it 'retrieves a Stat for a file using Authorization headers', (done) ->
+ @client.stat @textFile, httpCache: true, (error, stat) =>
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers
+ expect(@xhr.url).to.contain 'oauth_nonce'
+ else
+ expect(@xhr.headers).to.have.key 'Authorization'
+
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.isFile).to.equal true
+ expect(stat.versionTag).to.equal @textFileTag
+ expect(stat.size).to.equal @textFileData.length
+ if clientKeys.sandbox
+ expect(stat.inAppFolder).to.equal true
+ else
+ expect(stat.inAppFolder).to.equal false
+ done()
+
+ describe '#readdir', ->
+ it 'retrieves a Stat and entries for a folder', (done) ->
+ @client.readdir @testFolder, (error, entries, dir_stat, entry_stats) =>
+ expect(error).to.equal null
+ expect(entries).to.be.ok
+ expect(entries).to.have.length 2
+ expect(entries[0]).to.be.a 'string'
+ expect(entries[0]).not.to.have.string '/'
+ expect(entries[0]).to.match /^(test-binary-image.png)|(test-file.txt)$/
+ expect(dir_stat).to.be.instanceOf Dropbox.Stat
+ expect(dir_stat.path).to.equal @testFolder
+ expect(dir_stat.isFolder).to.equal true
+ expect(entry_stats).to.be.ok
+ expect(entry_stats).to.have.length 2
+ expect(entry_stats[0]).to.be.instanceOf Dropbox.Stat
+ expect(entry_stats[0].path).not.to.equal @testFolder
+ expect(entry_stats[0].path).to.have.string @testFolder
+ done()
+
+ describe 'with httpCache', ->
+ beforeEach ->
+ @xhr = null
+ @client.onXhr.addListener (xhr) =>
+ @xhr = xhr
+
+ it 'retrieves a folder Stat and entries using Authorization', (done) ->
+ @client.readdir @testFolder, httpCache: true,
+ (error, entries, dir_stat, entry_stats) =>
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers
+ expect(@xhr.url).to.contain 'oauth_nonce'
+ else
+ expect(@xhr.headers).to.have.key 'Authorization'
+
+ expect(error).to.equal null
+ expect(entries).to.be.ok
+ expect(entries).to.have.length 2
+ expect(entries[0]).to.be.a 'string'
+ expect(entries[0]).not.to.have.string '/'
+ expect(entries[0]).to.match(
+ /^(test-binary-image.png)|(test-file.txt)$/)
+ expect(dir_stat).to.be.instanceOf Dropbox.Stat
+ expect(dir_stat.path).to.equal @testFolder
+ expect(dir_stat.isFolder).to.equal true
+ expect(entry_stats).to.be.ok
+ expect(entry_stats).to.have.length 2
+ expect(entry_stats[0]).to.be.instanceOf Dropbox.Stat
+ expect(entry_stats[0].path).not.to.equal @testFolder
+ expect(entry_stats[0].path).to.have.string @testFolder
+ done()
+
+ describe '#history', ->
+ it 'gets a list of revisions', (done) ->
+ @client.history @textFile, (error, versions) =>
+ expect(error).to.equal null
+ expect(versions).to.have.length 1
+ expect(versions[0]).to.be.instanceOf Dropbox.Stat
+ expect(versions[0].path).to.equal @textFile
+ expect(versions[0].size).to.equal @textFileData.length
+ expect(versions[0].versionTag).to.equal @textFileTag
+ done()
+
+ it 'returns 40x if the limit is set to 0', (done) ->
+ listenerError = null
+ @client.onError.addListener (error) -> listenerError = error
+
+ @client.history @textFile, limit: 0, (error, versions) =>
+ expect(error).to.be.instanceOf Dropbox.ApiError
+ expect(listenerError).to.equal error
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do status codes.
+ expect(error.status).to.be.within 400, 499
+ expect(versions).not.to.be.ok
+ done()
+
+ describe 'with httpCache', ->
+ beforeEach ->
+ @xhr = null
+ @client.onXhr.addListener (xhr) =>
+ @xhr = xhr
+
+ it 'gets a list of revisions using Authorization headers', (done) ->
+ @client.history @textFile, httpCache: true, (error, versions) =>
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers
+ expect(@xhr.url).to.contain 'oauth_nonce'
+ else
+ expect(@xhr.headers).to.have.key 'Authorization'
+
+ expect(error).to.equal null
+ expect(versions).to.have.length 1
+ expect(versions[0]).to.be.instanceOf Dropbox.Stat
+ expect(versions[0].path).to.equal @textFile
+ expect(versions[0].size).to.equal @textFileData.length
+ expect(versions[0].versionTag).to.equal @textFileTag
+ done()
+
+ describe '#copy', ->
+ afterEach (done) ->
+ return done() unless @newFile
+ @client.remove @newFile, (error, stat) -> done()
+
+ it 'copies a file given by path', (done) ->
+ @timeout 12 * 1000 # This sequence is slow on the current API server.
+
+ @newFile = "#{@testFolder}/copy of test-file.txt"
+ @client.copy @textFile, @newFile, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ @client.readFile @textFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @textFile
+ expect(stat.versionTag).to.equal @textFileTag
+ done()
+
+ describe '#makeCopyReference', ->
+ afterEach (done) ->
+ return done() unless @newFile
+ @client.remove @newFile, (error, stat) -> done()
+
+ it 'creates a Dropbox.CopyReference that copies the file', (done) ->
+ @timeout 12 * 1000 # This sequence is slow on the current API server.
+ @newFile = "#{@testFolder}/ref copy of test-file.txt"
+
+ @client.makeCopyReference @textFile, (error, copyRef) =>
+ expect(error).to.equal null
+ expect(copyRef).to.be.instanceOf Dropbox.CopyReference
+ @client.copy copyRef, @newFile, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isFile).to.equal true
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ done()
+
+ describe '#move', ->
+ beforeEach (done) ->
+ @timeout 10 * 1000 # This sequence is slow on the current API server.
+ @moveFrom = "#{@testFolder}/move source of test-file.txt"
+ @client.copy @textFile, @moveFrom, (error, stat) ->
+ expect(error).to.equal null
+ done()
+
+ afterEach (done) ->
+ @client.remove @moveFrom, (error, stat) =>
+ return done() unless @moveTo
+ @client.remove @moveTo, (error, stat) -> done()
+
+ it 'moves a file', (done) ->
+ @timeout 15 * 1000 # This sequence is slow on the current API server.
+ @moveTo = "#{@testFolder}/moved test-file.txt"
+ @client.move @moveFrom, @moveTo, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @moveTo
+ expect(stat.isFile).to.equal true
+ @client.readFile @moveTo, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @moveTo
+ @client.readFile @moveFrom, (error, data, stat) ->
+ expect(error).to.be.ok
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do status codes.
+ expect(error).to.have.property 'status'
+ expect(error.status).to.equal 404
+ expect(data).to.equal undefined
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.equal undefined
+ done()
+
+ describe '#remove', ->
+ beforeEach (done) ->
+ @newFolder = "#{@testFolder}/folder delete test"
+ @client.mkdir @newFolder, (error, stat) =>
+ expect(error).to.equal null
+ done()
+
+ afterEach (done) ->
+ return done() unless @newFolder
+ @client.remove @newFolder, (error, stat) -> done()
+
+ it 'deletes a folder', (done) ->
+ @client.remove @newFolder, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFolder
+ @client.stat @newFolder, { removed: true }, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.isRemoved).to.equal true
+ done()
+
+ it 'deletes a folder when called as unlink', (done) ->
+ @client.unlink @newFolder, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFolder
+ @client.stat @newFolder, { removed: true }, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.isRemoved).to.equal true
+ done()
+
+ describe '#revertFile', ->
+ describe 'on a removed file', ->
+ beforeEach (done) ->
+ @timeout 12 * 1000 # This sequence seems to be quite slow.
+
+ @newFile = "#{@testFolder}/file revert test.txt"
+ @client.copy @textFile, @newFile, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ @versionTag = stat.versionTag
+ @client.remove @newFile, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ done()
+
+ afterEach (done) ->
+ return done() unless @newFile
+ @client.remove @newFile, (error, stat) -> done()
+
+ it 'reverts the file to a previous version', (done) ->
+ @timeout 12 * 1000 # This sequence seems to be quite slow.
+
+ @client.revertFile @newFile, @versionTag, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isRemoved).to.equal false
+ @client.readFile @newFile, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @newFile
+ expect(stat.isRemoved).to.equal false
+ done()
+
+ describe '#findByName', ->
+ it 'locates the test folder given a partial name', (done) ->
+ namePattern = @testFolder.substring 5
+ @client.search '/', namePattern, (error, matches) =>
+ expect(error).to.equal null
+ expect(matches).to.have.length 1
+ expect(matches[0]).to.be.instanceOf Dropbox.Stat
+ expect(matches[0].path).to.equal @testFolder
+ expect(matches[0].isFolder).to.equal true
+ done()
+
+ it 'lists the test folder files given the "test" pattern', (done) ->
+ @client.search @testFolder, 'test', (error, matches) =>
+ expect(error).to.equal null
+ expect(matches).to.have.length 2
+ done()
+
+ it 'only lists one match when given limit 1', (done) ->
+ @client.search @testFolder, 'test', limit: 1, (error, matches) =>
+ expect(error).to.equal null
+ expect(matches).to.have.length 1
+ done()
+
+ describe 'with httpCache', ->
+ beforeEach ->
+ @xhr = null
+ @client.onXhr.addListener (xhr) =>
+ @xhr = xhr
+
+ it 'locates the test folder using Authorize headers', (done) ->
+ namePattern = @testFolder.substring 5
+ @client.search '/', namePattern, httpCache: true, (error, matches) =>
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers
+ expect(@xhr.url).to.contain 'oauth_nonce'
+ else
+ expect(@xhr.headers).to.have.key 'Authorization'
+
+ expect(error).to.equal null
+ expect(matches).to.have.length 1
+ expect(matches[0]).to.be.instanceOf Dropbox.Stat
+ expect(matches[0].path).to.equal @testFolder
+ expect(matches[0].isFolder).to.equal true
+ done()
+
+ describe '#makeUrl', ->
+ describe 'for a short Web URL', ->
+ it 'returns a shortened Dropbox URL', (done) ->
+ @client.makeUrl @textFile, (error, publicUrl) ->
+ expect(error).to.equal null
+ expect(publicUrl).to.be.instanceOf Dropbox.PublicUrl
+ expect(publicUrl.isDirect).to.equal false
+ expect(publicUrl.url).to.contain '//db.tt/'
+ done()
+
+ describe 'for a Web URL created with long: true', ->
+ it 'returns an URL to a preview page', (done) ->
+ @client.makeUrl @textFile, { long: true }, (error, publicUrl) =>
+ expect(error).to.equal null
+ expect(publicUrl).to.be.instanceOf Dropbox.PublicUrl
+ expect(publicUrl.isDirect).to.equal false
+ expect(publicUrl.url).not.to.contain '//db.tt/'
+
+ # The cont/ents server does not return CORS headers.
+ return done() unless @nodejs
+ Dropbox.Xhr.request 'GET', publicUrl.url, {}, null, (error, data) ->
+ expect(error).to.equal null
+ expect(data).to.contain ''
+ done()
+
+ describe 'for a Web URL created with longUrl: true', ->
+ it 'returns an URL to a preview page', (done) ->
+ @client.makeUrl @textFile, { longUrl: true }, (error, publicUrl) =>
+ expect(error).to.equal null
+ expect(publicUrl).to.be.instanceOf Dropbox.PublicUrl
+ expect(publicUrl.isDirect).to.equal false
+ expect(publicUrl.url).not.to.contain '//db.tt/'
+ done()
+
+ describe 'for a direct download URL', ->
+ it 'gets a direct download URL', (done) ->
+ @client.makeUrl @textFile, { download: true }, (error, publicUrl) =>
+ expect(error).to.equal null
+ expect(publicUrl).to.be.instanceOf Dropbox.PublicUrl
+ expect(publicUrl.isDirect).to.equal true
+ expect(publicUrl.url).not.to.contain '//db.tt/'
+
+ # The contents server does not return CORS headers.
+ return done() unless @nodejs
+ Dropbox.Xhr.request 'GET', publicUrl.url, {}, null, (error, data) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ done()
+
+ describe 'for a direct download URL created with downloadHack: true', ->
+ it 'gets a direct long-lived download URL', (done) ->
+ @client.makeUrl @textFile, { downloadHack: true }, (error, publicUrl) =>
+ expect(error).to.equal null
+ expect(publicUrl).to.be.instanceOf Dropbox.PublicUrl
+ expect(publicUrl.isDirect).to.equal true
+ expect(publicUrl.url).not.to.contain '//db.tt/'
+ expect(publicUrl.expiresAt - Date.now()).to.be.above 86400000
+
+ # The download server does not return CORS headers.
+ return done() unless @nodejs
+ Dropbox.Xhr.request 'GET', publicUrl.url, {}, null, (error, data) =>
+ expect(error).to.equal null
+ expect(data).to.equal @textFileData
+ done()
+
+ describe '#pullChanges', ->
+ beforeEach ->
+ # Pulling an entire Dropbox can take a lot of time, so we need fancy
+ # logic here.
+ @timeoutValue = 60 * 1000
+ @timeout @timeoutValue
+
+ afterEach (done) ->
+ @timeoutValue += 10 * 1000
+ @timeout @timeoutValue
+ return done() unless @newFile
+ @client.remove @newFile, (error, stat) -> done()
+
+ it 'gets a cursor, then it gets relevant changes', (done) ->
+ @timeout @timeoutValue
+
+ @client.pullChanges (error, changes) =>
+ expect(error).to.equal null
+ expect(changes).to.be.instanceOf Dropbox.PulledChanges
+ expect(changes.blankSlate).to.equal true
+
+ # Calls pullChanges until it's done listing the user's Dropbox.
+ drainEntries = (client, callback) =>
+ return callback() unless changes.shouldPullAgain
+ @timeoutValue += 10 * 1000 # 10 extra seconds per call
+ @timeout @timeoutValue
+ client.pullChanges changes, (error, _changes) ->
+ expect(error).to.equal null
+ changes = _changes
+ drainEntries client, callback
+ drainEntries @client, =>
+
+ @newFile = "#{@testFolder}/delta-test.txt"
+ newFileData = "This file is used to test the pullChanges method.\n"
+ @client.writeFile @newFile, newFileData, (error, stat) =>
+ expect(error).to.equal null
+ expect(stat).to.have.property 'path'
+ expect(stat.path).to.equal @newFile
+
+ @client.pullChanges changes, (error, changes) =>
+ expect(error).to.equal null
+ expect(changes).to.be.instanceof Dropbox.PulledChanges
+ expect(changes.blankSlate).to.equal false
+ expect(changes.changes).to.have.length.greaterThan 0
+ change = changes.changes[changes.changes.length - 1]
+ expect(change).to.be.instanceOf Dropbox.PullChange
+ expect(change.path).to.equal @newFile
+ expect(change.wasRemoved).to.equal false
+ expect(change.stat.path).to.equal @newFile
+ done()
+
+ describe '#thumbnailUrl', ->
+ it 'produces an URL that contains the file name', ->
+ url = @client.thumbnailUrl @imageFile, { png: true, size: 'medium' }
+ expect(url).to.contain 'tests' # Fragment of the file name.
+ expect(url).to.contain 'png'
+ expect(url).to.contain 'medium'
+
+ describe '#readThumbnail', ->
+ it 'reads the image into a string', (done) ->
+ @timeout 12 * 1000 # Thumbnail generation is slow.
+ @client.readThumbnail @imageFile, { png: true }, (error, data, stat) =>
+ expect(error).to.equal null
+ expect(data).to.be.a 'string'
+ expect(data).to.contain 'PNG'
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @imageFile
+ expect(stat.isFile).to.equal true
+ done()
+
+ it 'reads the image into a Blob', (done) ->
+ return done() unless Blob?
+ @timeout 12 * 1000 # Thumbnail generation is slow.
+ options = { png: true, blob: true }
+ @client.readThumbnail @imageFile, options, (error, blob, stat) =>
+ expect(error).to.equal null
+ expect(blob).to.be.instanceOf Blob
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do headers.
+ expect(stat).to.be.instanceOf Dropbox.Stat
+ expect(stat.path).to.equal @imageFile
+ expect(stat.isFile).to.equal true
+ reader = new FileReader
+ reader.onloadend = =>
+ return unless reader.readyState == FileReader.DONE
+ buffer = reader.result
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.contain 'PNG'
+ done()
+ reader.readAsArrayBuffer blob
+
+ describe '#reset', ->
+ beforeEach ->
+ @authStates = []
+ @client.onAuthStateChange.addListener (client) =>
+ @authStates.push client.authState
+ @client.reset()
+
+ it 'gets the client into the RESET state', ->
+ expect(@client.authState).to.equal Dropbox.Client.RESET
+
+ it 'removes token and uid information', ->
+ credentials = @client.credentials()
+ expect(credentials).not.to.have.property 'token'
+ expect(credentials).not.to.have.property 'tokenSecret'
+ expect(credentials).not.to.have.property 'uid'
+
+ it 'triggers onAuthStateChange', ->
+ expect(@authStates).to.deep.equal [Dropbox.Client.RESET]
+
+ it 'does not trigger onAuthState if already reset', ->
+ @authStates = []
+ @client.reset()
+ expect(@authStates).to.deep.equal []
+
+ describe '#credentials', ->
+ it 'contains all the expected keys when DONE', ->
+ credentials = @client.credentials()
+ expect(credentials).to.have.property 'key'
+ expect(credentials).to.have.property 'sandbox'
+ expect(credentials).to.have.property 'token'
+ expect(credentials).to.have.property 'tokenSecret'
+ expect(credentials).to.have.property 'uid'
+
+ it 'does not return an authState when DONE', ->
+ credentials = @client.credentials()
+ expect(credentials).not.to.have.property 'authState'
+ expect(credentials).not.to.have.property 'secret'
+
+ it 'contains all the expected keys when RESET', ->
+ @client.reset()
+ credentials = @client.credentials()
+ expect(credentials).to.have.property 'key'
+ expect(credentials).to.have.property 'sandbox'
+
+ it 'does not return an authState when RESET', ->
+ @client.reset()
+ credentials = @client.credentials()
+ expect(credentials).not.to.have.property 'authState'
+ expect(credentials).not.to.have.property 'secret'
+
+ describe 'for a client with raw keys', ->
+ beforeEach ->
+ @client.setCredentials(
+ key: 'dpf43f3p2l4k3l03', secret: 'kd94hf93k423kf44',
+ token: 'user-token', tokenSecret: 'user-secret', uid: '1234567')
+
+ it 'contains all the expected keys when DONE', ->
+ credentials = @client.credentials()
+ expect(credentials).to.have.property 'key'
+ expect(credentials).to.have.property 'secret'
+ expect(credentials).to.have.property 'token'
+ expect(credentials).to.have.property 'tokenSecret'
+ expect(credentials).to.have.property 'uid'
+
+ it 'contains all the expected keys when RESET', ->
+ @client.reset()
+ credentials = @client.credentials()
+ expect(credentials).to.have.property 'key'
+ expect(credentials).to.have.property 'sandbox'
+ expect(credentials).to.have.property 'secret'
+
+
+ describe '#setCredentials', ->
+ it 'gets the client into the RESET state', ->
+ @client.setCredentials key: 'app-key', secret: 'app-secret'
+ expect(@client.authState).to.equal Dropbox.Client.RESET
+ credentials = @client.credentials()
+ expect(credentials.key).to.equal 'app-key'
+ expect(credentials.secret).to.equal 'app-secret'
+
+ it 'gets the client into the REQUEST state', ->
+ @client.setCredentials(
+ key: 'app-key', secret: 'app-secret', token: 'user-token',
+ tokenSecret: 'user-secret', authState: Dropbox.Client.REQUEST)
+ expect(@client.authState).to.equal Dropbox.Client.REQUEST
+ credentials = @client.credentials()
+ expect(credentials.key).to.equal 'app-key'
+ expect(credentials.secret).to.equal 'app-secret'
+ expect(credentials.token).to.equal 'user-token'
+ expect(credentials.tokenSecret).to.equal 'user-secret'
+
+ it 'gets the client into the DONE state', ->
+ @client.setCredentials(
+ key: 'app-key', secret: 'app-secret', token: 'user-token',
+ tokenSecret: 'user-secret', uid: '3141592')
+ expect(@client.authState).to.equal Dropbox.Client.DONE
+ credentials = @client.credentials()
+ expect(credentials.key).to.equal 'app-key'
+ expect(credentials.secret).to.equal 'app-secret'
+ expect(credentials.token).to.equal 'user-token'
+ expect(credentials.tokenSecret).to.equal 'user-secret'
+ expect(credentials.uid).to.equal '3141592'
+
+ beforeEach ->
+ @authStates = []
+ @client.onAuthStateChange.addListener (client) =>
+ @authStates.push client.authState
+
+ it 'triggers onAuthStateChange when switching from DONE to RESET', ->
+ @client.setCredentials key: 'app-key', secret: 'app-secret'
+ expect(@authStates).to.deep.equal [Dropbox.Client.RESET]
+
+ it 'does not trigger onAuthStateChange when not switching', ->
+ @client.setCredentials key: 'app-key', secret: 'app-secret'
+ @authStates = []
+ @client.setCredentials key: 'app-key', secret: 'app-secret'
+ expect(@authStates).to.deep.equal []
+
+ describe '#appHash', ->
+ it 'is consistent', ->
+ client = new Dropbox.Client clientKeys
+ expect(client.appHash()).to.equal @client.appHash()
+
+ it 'is a non-trivial string', ->
+ expect(@client.appHash()).to.be.a 'string'
+ expect(@client.appHash().length).to.be.greaterThan 4
+
+ describe '#isAuthenticated', ->
+ it 'is true for a client with full tokens', ->
+ expect(@client.isAuthenticated()).to.equal true
+
+ it 'is false for a freshly reset client', ->
+ @client.reset()
+ expect(@client.isAuthenticated()).to.equal false
+
+ describe '#authenticate', ->
+ it 'fails to move from RESET without an OAuth driver', ->
+ @client.reset()
+ @client.authDriver null
+ expect(=> @client.authenticate(->
+ expect('authentication completed').to.equal false
+ )).to.throw(Error, /authDriver/)
+
+ it 'completes without an OAuth driver if already in DONE', (done) ->
+ @client.authDriver null
+ @client.authenticate (error, client) =>
+ expect(error).to.equal null
+ expect(client).to.equal @client
+ done()
+
+describe 'Dropbox.Client', ->
+ describe 'with full Dropbox access', ->
+ buildClientTests testFullDropboxKeys
+
+ describe 'with Folder access', ->
+ buildClientTests testKeys
+
+ describe '#authenticate + #signOut', ->
+ # NOTE: we're not duplicating this test in the full Dropbox acess suite,
+ # because it's annoying to the tester
+ it 'completes the authenticate flow', (done) ->
+ @timeout 45 * 1000 # Time-consuming because the user must click.
+ @client.reset()
+ @client.authDriver authDriver
+ authStateChanges = ['authorize']
+ @client.onAuthStateChange.addListener (client) ->
+ authStateChanges.push client.authState
+ @client.authenticate (error, client) =>
+ expect(error).to.equal null
+ expect(client).to.equal @client
+ expect(client.authState).to.equal Dropbox.Client.DONE
+ expect(client.isAuthenticated()).to.equal true
+ expect(authStateChanges).to.deep.equal(['authorize',
+ Dropbox.Client.REQUEST, Dropbox.Client.AUTHORIZED,
+ Dropbox.Client.DONE])
+ # Verify that we can do API calls.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ invalidCredentials = client.credentials()
+ authStateChanges = ['signOff']
+ client.signOut (error) ->
+ expect(error).to.equal null
+ expect(client.authState).to.equal Dropbox.Client.SIGNED_OFF
+ expect(client.isAuthenticated()).to.equal false
+ expect(authStateChanges).to.deep.equal(['signOff',
+ Dropbox.Client.SIGNED_OFF])
+ # Verify that we can't use the old token in API calls.
+ invalidClient = new Dropbox.Client invalidCredentials
+ invalidClient.getUserInfo (error, userInfo) ->
+ expect(error).to.be.ok
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do error codes.
+ expect(error.status).to.equal 401
+ # Verify that the same client can be used for a 2nd signin.
+ authStateChanges = ['authorize2']
+ client.authenticate (error, client) ->
+ expect(error).to.equal null
+ expect(client.authState).to.equal Dropbox.Client.DONE
+ expect(client.isAuthenticated()).to.equal true
+ expect(authStateChanges).to.deep.equal(['authorize2',
+ Dropbox.Client.RESET, Dropbox.Client.REQUEST,
+ Dropbox.Client.AUTHORIZED, Dropbox.Client.DONE])
+ # Verify that we can do API calls after the 2nd signin.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ done()
+
+
+ describe '#appHash', ->
+ it 'depends on the app key', ->
+ client = new Dropbox.Client testFullDropboxKeys
+ expect(client.appHash()).not.to.equal @client.appHash()
+
+ describe '#constructor', ->
+ it 'raises an Error if initialized without an API key / secret', ->
+ expect(-> new Dropbox.Client(token: '123', tokenSecret: '456')).to.
+ throw(Error, /no api key/i)
+
diff --git a/lib/client/storage/dropbox/test/src/drivers_test.coffee b/lib/client/storage/dropbox/test/src/drivers_test.coffee
new file mode 100644
index 00000000..d35e8485
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/drivers_test.coffee
@@ -0,0 +1,446 @@
+describe 'Dropbox.Drivers.BrowserBase', ->
+ beforeEach ->
+ @node_js = module? and module?.exports? and require?
+ @chrome_app = chrome? and (chrome.extension or chrome.app)
+ @client = new Dropbox.Client testKeys
+
+ describe 'with rememberUser: false', ->
+ beforeEach (done) ->
+ return done() if @node_js or @chrome_app
+ @driver = new Dropbox.Drivers.BrowserBase
+ @driver.setStorageKey @client
+ @driver.forgetCredentials done
+
+ afterEach (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.forgetCredentials done
+
+ describe '#loadCredentials', ->
+ it 'produces the credentials passed to storeCredentials', (done) ->
+ return done() if @node_js or @chrome_app
+ goldCredentials = @client.credentials()
+ @driver.storeCredentials goldCredentials, =>
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.deep.equal goldCredentials
+ done()
+
+ it 'produces null after forgetCredentials was called', (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.storeCredentials @client.credentials(), =>
+ @driver.forgetCredentials =>
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ it 'produces null if a different scope is provided', (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.setStorageKey @client
+ @driver.storeCredentials @client.credentials(), =>
+ @driver.forgetCredentials =>
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+
+describe 'Dropbox.Drivers.Redirect', ->
+ describe '#url', ->
+ beforeEach ->
+ @stub = sinon.stub Dropbox.Drivers.BrowserBase, 'currentLocation'
+ afterEach ->
+ @stub.restore()
+
+ it 'adds a query string to a static URL', ->
+ @stub.returns 'http://test/file'
+ driver = new Dropbox.Drivers.Redirect useQuery: true
+ expect(driver.url()).to.
+ equal 'http://test/file?_dropboxjs_scope=default'
+
+ it 'adds a fragment to a static URL', ->
+ @stub.returns 'http://test/file'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.url()).to.
+ equal 'http://test/file#?_dropboxjs_scope=default'
+
+ it 'adds a query param to a URL with a query string', ->
+ @stub.returns 'http://test/file?a=true'
+ driver = new Dropbox.Drivers.Redirect useQuery: true
+ expect(driver.url()).to.
+ equal 'http://test/file?a=true&_dropboxjs_scope=default'
+
+ it 'adds a fragment to a URL with a query string', ->
+ @stub.returns 'http://test/file?a=true'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.url()).to.
+ equal 'http://test/file?a=true#?_dropboxjs_scope=default'
+
+ it 'adds a query string to a static URL with a fragment', ->
+ @stub.returns 'http://test/file#frag'
+ driver = new Dropbox.Drivers.Redirect useQuery: true
+ expect(driver.url()).to.
+ equal 'http://test/file?_dropboxjs_scope=default#frag'
+
+ it 'replaces the fragment in a static URL with a fragment', ->
+ @stub.returns 'http://test/file#frag'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.url()).to.
+ equal 'http://test/file#?_dropboxjs_scope=default'
+
+ it 'adds a query param to a URL with a query string and fragment', ->
+ @stub.returns 'http://test/file?a=true#frag'
+ driver = new Dropbox.Drivers.Redirect useQuery: true
+ expect(driver.url()).to.
+ equal 'http://test/file?a=true&_dropboxjs_scope=default#frag'
+
+ it 'replaces the fragment in a URL with a query string and fragment', ->
+ @stub.returns 'http://test/file?a=true#frag'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.url()).to.
+ equal 'http://test/file?a=true#?_dropboxjs_scope=default'
+
+ it 'obeys the scope option', ->
+ @stub.returns 'http://test/file'
+ driver = new Dropbox.Drivers.Redirect(
+ scope: 'not default', useQuery: true)
+ expect(driver.url()).to.
+ equal 'http://test/file?_dropboxjs_scope=not%20default'
+
+ it 'obeys the scope option when adding a fragment', ->
+ @stub.returns 'http://test/file'
+ driver = new Dropbox.Drivers.Redirect scope: 'not default'
+ expect(driver.url()).to.
+ equal 'http://test/file#?_dropboxjs_scope=not%20default'
+
+ describe '#locationToken', ->
+ beforeEach ->
+ @stub = sinon.stub Dropbox.Drivers.BrowserBase, 'currentLocation'
+ afterEach ->
+ @stub.restore()
+
+ it 'returns null if the location does not contain the arg', ->
+ @stub.returns 'http://test/file?_dropboxjs_scope=default& ' +
+ 'another_token=ab%20cd&oauth_tok=en'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.locationToken()).to.equal null
+
+ it 'returns null if the location fragment does not contain the arg', ->
+ @stub.returns 'http://test/file#?_dropboxjs_scope=default& ' +
+ 'another_token=ab%20cd&oauth_tok=en'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.locationToken()).to.equal null
+
+ it "extracts the token successfully with default scope", ->
+ @stub.returns 'http://test/file?_dropboxjs_scope=default&' +
+ 'oauth_token=ab%20cd&other_param=true'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.locationToken()).to.equal 'ab cd'
+
+ it "extracts the token successfully with set scope", ->
+ @stub.returns 'http://test/file?_dropboxjs_scope=not%20default&' +
+ 'oauth_token=ab%20cd'
+ driver = new Dropbox.Drivers.Redirect scope: 'not default'
+ expect(driver.locationToken()).to.equal 'ab cd'
+
+ it "extracts the token from fragment with default scope", ->
+ @stub.returns 'http://test/file#?_dropboxjs_scope=default&' +
+ 'oauth_token=ab%20cd&other_param=true'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.locationToken()).to.equal 'ab cd'
+
+ it "extracts the token from fragment with set scope", ->
+ @stub.returns 'http://test/file#?_dropboxjs_scope=not%20default&' +
+ 'oauth_token=ab%20cd'
+ driver = new Dropbox.Drivers.Redirect scope: 'not default'
+ expect(driver.locationToken()).to.equal 'ab cd'
+
+ it "returns null if the location scope doesn't match", ->
+ @stub.returns 'http://test/file?_dropboxjs_scope=defaultx&oauth_token=ab'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.locationToken()).to.equal null
+
+ it "returns null if the location fragment scope doesn't match", ->
+ @stub.returns 'http://test/file#?_dropboxjs_scope=defaultx&oauth_token=a'
+ driver = new Dropbox.Drivers.Redirect
+ expect(driver.locationToken()).to.equal null
+
+ describe '#loadCredentials', ->
+ beforeEach ->
+ @node_js = module? and module.exports? and require?
+ @chrome_app = chrome? and (chrome.extension or chrome.app?.runtime)
+ return if @node_js or @chrome_app
+ @client = new Dropbox.Client testKeys
+ @driver = new Dropbox.Drivers.Redirect scope: 'some_scope'
+ @driver.setStorageKey @client
+
+ it 'produces the credentials passed to storeCredentials', (done) ->
+ return done() if @node_js or @chrome_app
+ goldCredentials = @client.credentials()
+ @driver.storeCredentials goldCredentials, =>
+ @driver = new Dropbox.Drivers.Redirect scope: 'some_scope'
+ @driver.setStorageKey @client
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.deep.equal goldCredentials
+ done()
+
+ it 'produces null after forgetCredentials was called', (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.storeCredentials @client.credentials(), =>
+ @driver.forgetCredentials =>
+ @driver = new Dropbox.Drivers.Redirect scope: 'some_scope'
+ @driver.setStorageKey @client
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ it 'produces null if a different scope is provided', (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.setStorageKey @client
+ @driver.storeCredentials @client.credentials(), =>
+ @driver = new Dropbox.Drivers.Redirect scope: 'other_scope'
+ @driver.setStorageKey @client
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ describe 'integration', ->
+ beforeEach ->
+ @node_js = module? and module.exports? and require?
+ @chrome_app = chrome? and (chrome.extension or chrome.app?.runtime)
+
+ it 'should work', (done) ->
+ return done() if @node_js or @chrome_app
+ @timeout 30 * 1000 # Time-consuming because the user must click.
+
+ listener = (event) ->
+ expect(event.data).to.match(/^\[.*\]$/)
+ [error, credentials] = JSON.parse event.data
+ expect(error).to.equal null
+ expect(credentials).to.have.property 'uid'
+ expect(credentials.uid).to.be.a 'string'
+ window.removeEventListener 'message', listener
+ done()
+
+ window.addEventListener 'message', listener
+ (new Dropbox.Drivers.Popup()).openWindow(
+ '/test/html/redirect_driver_test.html')
+
+describe 'Dropbox.Drivers.Popup', ->
+ describe '#url', ->
+ beforeEach ->
+ @stub = sinon.stub Dropbox.Drivers.BrowserBase, 'currentLocation'
+ @stub.returns 'http://test:123/a/path/file.htmx'
+
+ afterEach ->
+ @stub.restore()
+
+ it 'reflects the current page when there are no options', ->
+ driver = new Dropbox.Drivers.Popup
+ expect(driver.url()).to.equal 'http://test:123/a/path/file.htmx'
+
+ it 'replaces the current file correctly', ->
+ driver = new Dropbox.Drivers.Popup receiverFile: 'another.file'
+ expect(driver.url()).to.equal 'http://test:123/a/path/another.file#'
+
+ it 'replaces the current file without a fragment correctly', ->
+ driver = new Dropbox.Drivers.Popup
+ receiverFile: 'another.file', noFragment: true
+ expect(driver.url()).to.equal 'http://test:123/a/path/another.file'
+
+ it 'replaces an entire URL without a fragment correctly', ->
+ driver = new Dropbox.Drivers.Popup
+ receiverUrl: 'https://something.com/filez'
+ expect(driver.url()).to.equal 'https://something.com/filez#'
+
+ it 'replaces an entire URL with a fragment correctly', ->
+ driver = new Dropbox.Drivers.Popup
+ receiverUrl: 'https://something.com/filez#frag'
+ expect(driver.url()).to.equal 'https://something.com/filez#frag'
+
+ it 'replaces an entire URL without a fragment and useQuery correctly', ->
+ driver = new Dropbox.Drivers.Popup
+ receiverUrl: 'https://something.com/filez', noFragment: true
+ expect(driver.url()).to.equal 'https://something.com/filez'
+
+ describe '#loadCredentials', ->
+ beforeEach ->
+ @node_js = module? and module.exports? and require?
+ @chrome_app = chrome? and (chrome.extension or chrome.app?.runtime)
+ return if @node_js or @chrome_app
+ @client = new Dropbox.Client testKeys
+ @driver = new Dropbox.Drivers.Popup scope: 'some_scope'
+ @driver.setStorageKey @client
+
+ it 'produces the credentials passed to storeCredentials', (done) ->
+ return done() if @node_js or @chrome_app
+ goldCredentials = @client.credentials()
+ @driver.storeCredentials goldCredentials, =>
+ @driver = new Dropbox.Drivers.Popup scope: 'some_scope'
+ @driver.setStorageKey @client
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.deep.equal goldCredentials
+ done()
+
+ it 'produces null after forgetCredentials was called', (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.storeCredentials @client.credentials(), =>
+ @driver.forgetCredentials =>
+ @driver = new Dropbox.Drivers.Popup scope: 'some_scope'
+ @driver.setStorageKey @client
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ it 'produces null if a different scope is provided', (done) ->
+ return done() if @node_js or @chrome_app
+ @driver.setStorageKey @client
+ @driver.storeCredentials @client.credentials(), =>
+ @driver = new Dropbox.Drivers.Popup scope: 'other_scope'
+ @driver.setStorageKey @client
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ describe 'integration', ->
+ beforeEach ->
+ @node_js = module? and module.exports? and require?
+ @chrome_app = chrome? and (chrome.extension or chrome.app?.runtime)
+
+ it 'should work with a query string', (done) ->
+ return done() if @node_js or @chrome_app
+ @timeout 45 * 1000 # Time-consuming because the user must click.
+
+ client = new Dropbox.Client testKeys
+ client.reset()
+ authDriver = new Dropbox.Drivers.Popup
+ receiverFile: 'oauth_receiver.html', noFragment: true,
+ scope: 'popup-integration', rememberUser: false
+ client.authDriver authDriver
+ client.authenticate (error, client) =>
+ expect(error).to.equal null
+ expect(client.authState).to.equal Dropbox.Client.DONE
+ # Verify that we can do API calls.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+
+ # Follow-up authenticate() should restart the process.
+ client.reset()
+ authDriver.doAuthorize = (authUrl, token, tokenSecret, callback) ->
+ client.reset()
+ done()
+ client.authenticate ->
+ assert false, 'The second authenticate() should not complete.'
+
+ it 'should work with a URL fragment and rememberUser: true', (done) ->
+ return done() if @node_js or @chrome_app
+ @timeout 45 * 1000 # Time-consuming because the user must click.
+
+ client = new Dropbox.Client testKeys
+ client.reset()
+ authDriver = new Dropbox.Drivers.Popup
+ receiverFile: 'oauth_receiver.html', noFragment: false,
+ scope: 'popup-integration', rememberUser: true
+ client.authDriver authDriver
+ authDriver.setStorageKey client
+ authDriver.forgetCredentials ->
+ client.authenticate (error, client) ->
+ expect(error).to.equal null
+ expect(client.authState).to.equal Dropbox.Client.DONE
+ # Verify that we can do API calls.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+
+ # Follow-up authenticate() should use stored credentials.
+ client.reset()
+ authDriver.doAuthorize = (authUrl, token, tokenSecret, callback) ->
+ assert false,
+ 'Stored credentials not used in second authenticate()'
+ client.authenticate (error, client) ->
+ # Verify that we can do API calls.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ done()
+
+describe 'Dropbox.Drivers.Chrome', ->
+ beforeEach ->
+ @chrome_app = chrome? and (chrome.extension or chrome.app?.runtime)
+ @client = new Dropbox.Client testKeys
+
+ describe '#url', ->
+ beforeEach ->
+ return unless @chrome_app
+ @path = 'test/html/redirect_driver_test.html'
+ @driver = new Dropbox.Drivers.Chrome receiverPath: @path
+
+ it 'produces a chrome-extension:// url', ->
+ return unless @chrome_app
+ expect(@driver.url()).to.match(/^chrome-extension:\/\//)
+
+ it 'produces an URL ending in redirectPath', ->
+ return unless @chrome_app
+ url = @driver.url()
+ expect(url.substring(url.length - @path.length)).to.equal @path
+
+ describe '#loadCredentials', ->
+ beforeEach ->
+ return unless @chrome_app
+ @client = new Dropbox.Client testKeys
+ @driver = new Dropbox.Drivers.Chrome scope: 'some_scope'
+
+ it 'produces the credentials passed to storeCredentials', (done) ->
+ return done() unless @chrome_app
+ goldCredentials = @client.credentials()
+ @driver.storeCredentials goldCredentials, =>
+ @driver = new Dropbox.Drivers.Chrome scope: 'some_scope'
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.deep.equal goldCredentials
+ done()
+
+ it 'produces null after forgetCredentials was called', (done) ->
+ return done() unless @chrome_app
+ @driver.storeCredentials @client.credentials(), =>
+ @driver.forgetCredentials =>
+ @driver = new Dropbox.Drivers.Chrome scope: 'some_scope'
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ it 'produces null if a different scope is provided', (done) ->
+ return done() unless @chrome_app
+ @driver.storeCredentials @client.credentials(), =>
+ @driver = new Dropbox.Drivers.Chrome scope: 'other_scope'
+ @driver.loadCredentials (credentials) ->
+ expect(credentials).to.equal null
+ done()
+
+ describe 'integration', ->
+ it 'should work', (done) ->
+ return done() unless @chrome_app
+ @timeout 45 * 1000 # Time-consuming because the user must click.
+
+ client = new Dropbox.Client testKeys
+ client.reset()
+ authDriver = new Dropbox.Drivers.Chrome(
+ receiverPath: 'test/html/chrome_oauth_receiver.html',
+ scope: 'chrome_integration')
+ client.authDriver authDriver
+ authDriver.forgetCredentials ->
+ client.authenticate (error, client) ->
+ expect(error).to.equal null
+ expect(client.authState).to.equal Dropbox.Client.DONE
+ # Verify that we can do API calls.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ # Follow-up authenticate() should use stored credentials.
+ client.reset()
+ authDriver.doAuthorize = (authUrl, token, tokenSecret, callback) ->
+ assert false,
+ 'Stored credentials not used in second authenticate()'
+ client.authenticate (error, client) ->
+ # Verify that we can do API calls.
+ client.getUserInfo (error, userInfo) ->
+ expect(error).to.equal null
+ expect(userInfo).to.be.instanceOf Dropbox.UserInfo
+ done()
diff --git a/lib/client/storage/dropbox/test/src/event_source_test.coffee b/lib/client/storage/dropbox/test/src/event_source_test.coffee
new file mode 100644
index 00000000..e7440b0f
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/event_source_test.coffee
@@ -0,0 +1,144 @@
+describe 'Dropbox.EventSource', ->
+ beforeEach ->
+ @source = new Dropbox.EventSource
+ @cancelable = new Dropbox.EventSource cancelable: true
+
+ # 3 listeners, 1 and 2 are already hooked up
+ @event1 = null
+ @return1 = true
+ @listener1 = (event) =>
+ @event1 = event
+ @return1
+ @source.addListener @listener1
+ @cancelable.addListener @listener1
+ @event2 = null
+ @return2 = false
+ @listener2 = (event) =>
+ @event2 = event
+ @return2
+ @source.addListener @listener2
+ @cancelable.addListener @listener2
+ @event3 = null
+ @return3 = true
+ @listener3 = (event) =>
+ @event3 = event
+ @return3
+
+ describe '#addListener', ->
+ it 'adds a new listener', ->
+ @source.addListener @listener3
+ expect(@source._listeners).to.deep.
+ equal [@listener1, @listener2, @listener3]
+
+ it 'does not add an existing listener', ->
+ @source.addListener @listener2
+ expect(@source._listeners).to.deep.equal [@listener1, @listener2]
+
+ it 'is idempotent', ->
+ @source.addListener @listener3
+ @source.addListener @listener3
+ expect(@source._listeners).to.deep.
+ equal [@listener1, @listener2, @listener3]
+
+ it 'refuses to add non-functions', ->
+ expect(=> @source.addListener 42).to.throw(TypeError, /listener type/)
+
+ describe '#removeListener', ->
+ it 'does nothing for a non-existing listener', ->
+ @source.removeListener @listener3
+ expect(@source._listeners).to.deep.equal [@listener1, @listener2]
+
+ it 'removes a listener at the end of the queue', ->
+ @source.removeListener @listener2
+ expect(@source._listeners).to.deep.equal [@listener1]
+
+ it 'removes a listener at the beginning of the queue', ->
+ @source.removeListener @listener1
+ expect(@source._listeners).to.deep.equal [@listener2]
+
+ it 'removes a listener at the middle of the queue', ->
+ @source.addListener @listener3
+ @source.removeListener @listener2
+ expect(@source._listeners).to.deep.equal [@listener1, @listener3]
+
+ it 'removes all the listeners', ->
+ @source.removeListener @listener1
+ @source.removeListener @listener2
+ expect(@source._listeners).to.deep.equal []
+
+ describe 'without ES5 Array#indexOf', ->
+ beforeEach ->
+ @source._listeners.indexOf = null
+
+ afterEach ->
+ delete @source._listeners.indexOf
+
+ assertArraysEqual = (array1, array2) ->
+ expect(array1.length).to.equal(array2.length)
+ for i in [0...array1.length]
+ expect(array1[i]).to.equal(array2[i])
+
+ it 'does nothing for a non-existing listener', ->
+ @source.removeListener @listener3
+ assertArraysEqual @source._listeners, [@listener1, @listener2]
+
+ it 'removes a listener at the end of the queue', ->
+ @source.removeListener @listener2
+ assertArraysEqual @source._listeners, [@listener1]
+
+ it 'removes a listener at the beginning of the queue', ->
+ @source.removeListener @listener1
+ assertArraysEqual @source._listeners, [@listener2]
+
+ it 'removes a listener at the middle of the queue', ->
+ @source.addListener @listener3
+ @source.removeListener @listener2
+ assertArraysEqual @source._listeners, [@listener1, @listener3]
+
+ it 'removes all the listeners', ->
+ @source.removeListener @listener1
+ @source.removeListener @listener2
+ assertArraysEqual @source._listeners, []
+
+ describe '#dispatch', ->
+ beforeEach ->
+ @event = { answer: 42 }
+
+ it 'passes event to all listeners', ->
+ @source.dispatch @event
+ expect(@event1).to.equal @event
+ expect(@event2).to.equal @event
+ expect(@event3).to.equal null
+
+ describe 'on non-cancelable events', ->
+ beforeEach ->
+ @source.addListener @listener3
+ @returnValue = @source.dispatch @event
+
+ it 'calls all the listeners', ->
+ expect(@event1).to.equal @event
+ expect(@event2).to.equal @event
+ expect(@event3).to.equal @event
+
+ it 'ignores the listener return values', ->
+ expect(@returnValue).to.equal true
+
+ describe 'on cancelable events', ->
+ beforeEach ->
+ @cancelable.addListener @listener3
+ @returnValue = @cancelable.dispatch @event
+
+ it 'stops calling listeners after cancelation', ->
+ expect(@event1).to.equal @event
+ expect(@event2).to.equal @event
+ expect(@event3).to.equal null
+
+ it 'reports cancelation', ->
+ expect(@returnValue).to.equal false
+
+ it 'calls all listeners if no cancelation occurs', ->
+ @return2 = true
+ @returnValue = @cancelable.dispatch @event
+
+ expect(@returnValue).to.equal true
+ expect(@event3).to.equal @event
diff --git a/lib/client/storage/dropbox/test/src/helper.coffee b/lib/client/storage/dropbox/test/src/helper.coffee
new file mode 100644
index 00000000..2b42b86a
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/helper.coffee
@@ -0,0 +1,55 @@
+if global? and require? and module?
+ # Node.JS
+ exports = global
+
+ exports.Dropbox = require '../../lib/dropbox'
+ exports.chai = require 'chai'
+ exports.sinon = require 'sinon'
+ exports.sinonChai = require 'sinon-chai'
+
+ exports.authDriver = new Dropbox.Drivers.NodeServer port: 8912
+
+ TokenStash = require './token_stash.js'
+ (new TokenStash()).get (credentials) ->
+ exports.testKeys = credentials
+ (new TokenStash(fullDropbox: true)).get (credentials) ->
+ exports.testFullDropboxKeys = credentials
+
+ testIconPath = './test/binary/dropbox.png'
+ fs = require 'fs'
+ buffer = fs.readFileSync testIconPath
+ bytes = []
+ for i in [0...buffer.length]
+ bytes.push String.fromCharCode(buffer.readUInt8(i))
+ exports.testImageBytes = bytes.join ''
+ exports.testImageUrl = 'http://localhost:8913/favicon.ico'
+ imageServer = null
+ exports.testImageServerOn = ->
+ imageServer =
+ new Dropbox.Drivers.NodeServer port: 8913, favicon: testIconPath
+ exports.testImageServerOff = ->
+ imageServer.closeServer()
+ imageServer = null
+else
+ if chrome? and chrome.runtime
+ # Chrome app
+ exports = window
+ exports.authDriver = new Dropbox.Drivers.Chrome(
+ receiverPath: 'test/html/chrome_oauth_receiver.html',
+ scope: 'helper-chrome')
+ # Hack-implement "rememberUser: false" in the Chrome driver.
+ exports.authDriver.storeCredentials = (credentials, callback) -> callback()
+ exports.authDriver.loadCredentials = (callback) -> callback null
+ else
+ # Browser
+ exports = window
+ exports.authDriver = new Dropbox.Drivers.Popup(
+ receiverFile: 'oauth_receiver.html', scope: 'helper-popup')
+
+ exports.testImageUrl = '/test/binary/dropbox.png'
+ exports.testImageServerOn = -> null
+ exports.testImageServerOff = -> null
+
+# Shared setup.
+exports.assert = exports.chai.assert
+exports.expect = exports.chai.expect
diff --git a/lib/client/storage/dropbox/test/src/hmac_test.coffee b/lib/client/storage/dropbox/test/src/hmac_test.coffee
new file mode 100644
index 00000000..b8fbafce
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/hmac_test.coffee
@@ -0,0 +1,22 @@
+describe 'Dropbox.hmac', ->
+ it 'works for an empty message with an empty key', ->
+ expect(Dropbox.hmac('', '')).to.equal '+9sdGxiqbAgyS31ktx+3Y3BpDh0='
+
+ it 'works for the non-empty Wikipedia example', ->
+ expect(Dropbox.hmac('The quick brown fox jumps over the lazy dog', 'key')).
+ to.equal '3nybhbi3iqa8ino29wqQcBydtNk='
+
+ it 'works for the Oauth example', ->
+ key = 'kd94hf93k423kf44&pfkkdhi9sl3r4s00'
+ string = 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal'
+ expect(Dropbox.hmac(string, key)).to.equal 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='
+
+describe 'Dropbox.sha1', ->
+ it 'works for an empty message', ->
+ expect(Dropbox.sha1('')).to.equal '2jmj7l5rSw0yVb/vlWAYkK/YBwk='
+ it 'works for the FIPS-180 Appendix A sample', ->
+ expect(Dropbox.sha1('abc')).to.equal 'qZk+NkcGgWq6PiVxeFDCbJzQ2J0='
+ it 'works for the FIPS-180 Appendix B sample', ->
+ string = 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'
+ expect(Dropbox.sha1(string)).to.equal 'hJg+RBw70m66rkqh+VEp5eVGcPE='
+
diff --git a/lib/client/storage/dropbox/test/src/oauth_test.coffee b/lib/client/storage/dropbox/test/src/oauth_test.coffee
new file mode 100644
index 00000000..25dbc562
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/oauth_test.coffee
@@ -0,0 +1,124 @@
+describe 'Dropbox.Oauth', ->
+ beforeEach ->
+ @oauth = new Dropbox.Oauth
+ key: 'dpf43f3p2l4k3l03',
+ secret: 'kd94hf93k423kf44'
+ @oauth.setToken 'nnch734d00sl2jdk', 'pfkkdhi9sl3r4s00'
+
+ # The example in OAuth 1.0a Appendix A.
+ @request =
+ method: 'GET',
+ url: 'http://photos.example.net/photos'
+ params:
+ file: 'vacation.jpg',
+ size: 'original'
+ @dateStub = sinon.stub Date, 'now'
+ @dateStub.returns 1191242096999
+
+ afterEach ->
+ @dateStub.restore()
+
+ describe '#boilerplateParams', ->
+ it 'issues unique nonces', ->
+ nonces = {}
+ for i in [1..100]
+ nonce = @oauth.boilerplateParams({}).oauth_nonce
+ expect(nonces).not.to.have.property nonce
+ nonces[nonce] = true
+
+ it 'fills all the arguments', ->
+ params = @oauth.boilerplateParams(@request.params)
+ properties = ['oauth_consumer_key', 'oauth_nonce',
+ 'oauth_signature_method', 'oauth_timestamp',
+ 'oauth_version']
+ for property in properties
+ expect(params).to.have.property property
+
+ describe '#signature', ->
+ it 'works for the OAuth 1.0a example', ->
+ @nonceStub = sinon.stub @oauth, 'nonce'
+ @nonceStub.returns 'kllo9940pd9333jh'
+
+ @oauth.boilerplateParams(@request.params)
+ expect(@oauth.signature(@request.method, @request.url, @request.params)).
+ to.equal 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='
+
+ @nonceStub.restore()
+
+ it 'works with an encoded key', ->
+ @oauth = new Dropbox.Oauth
+ key: Dropbox.encodeKey(@oauth.key, @oauth.secret),
+ token: @oauth.token, tokenSecret: @oauth.tokenSecret
+
+ @nonceStub = sinon.stub @oauth, 'nonce'
+ @nonceStub.returns 'kllo9940pd9333jh'
+
+ @oauth.boilerplateParams(@request.params)
+ expect(@oauth.signature(@request.method, @request.url, @request.params)).
+ to.equal 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='
+
+ @nonceStub.restore()
+
+ describe '#addAuthParams', ->
+ it 'matches the OAuth 1.0a example', ->
+ @nonceStub = sinon.stub @oauth, 'nonce'
+ @nonceStub.returns 'kllo9940pd9333jh'
+
+ goldenParams =
+ file: 'vacation.jpg'
+ oauth_consumer_key: 'dpf43f3p2l4k3l03'
+ oauth_nonce: 'kllo9940pd9333jh'
+ oauth_signature: 'tR3+Ty81lMeYAr/Fid0kMTYa/WM='
+ oauth_signature_method: 'HMAC-SHA1'
+ oauth_timestamp: '1191242096'
+ oauth_token: 'nnch734d00sl2jdk'
+ oauth_version: '1.0'
+ size: 'original'
+
+ @oauth.addAuthParams @request.method, @request.url, @request.params
+ expect(Dropbox.Xhr.urlEncode(@request.params)).to.
+ eql Dropbox.Xhr.urlEncode(goldenParams)
+
+ @nonceStub.restore()
+
+ it "doesn't leave any OAuth-related value in params", ->
+ @oauth.authHeader(@request.method, @request.url, @request.params)
+ expect(Dropbox.Xhr.urlEncode(@request.params)).to.
+ equal "file=vacation.jpg&size=original"
+
+ describe '#authHeader', ->
+ it 'matches the OAuth 1.0a example', ->
+ @nonceStub = sinon.stub @oauth, 'nonce'
+ @nonceStub.returns 'kllo9940pd9333jh'
+
+ goldenHeader = 'OAuth oauth_consumer_key="dpf43f3p2l4k3l03",oauth_nonce="kllo9940pd9333jh",oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1191242096",oauth_token="nnch734d00sl2jdk",oauth_version="1.0"'
+ header = @oauth.authHeader @request.method, @request.url, @request.params
+ expect(header).to.equal goldenHeader
+
+ @nonceStub.restore()
+
+ it "doesn't leave any OAuth-related value in params", ->
+ @oauth.authHeader(@request.method, @request.url, @request.params)
+ expect(Dropbox.Xhr.urlEncode(@request.params)).to.
+ equal "file=vacation.jpg&size=original"
+
+ describe '#appHash', ->
+ it 'is a non-trivial string', ->
+ expect(@oauth.appHash()).to.be.a 'string'
+ expect(@oauth.appHash().length).to.be.greaterThan 4
+
+ it 'is consistent', ->
+ oauth = new Dropbox.Oauth key: @oauth.key, secret: @oauth.secret
+ expect(oauth.appHash()).to.equal @oauth.appHash()
+
+ it 'depends on the app key', ->
+ oauth = new Dropbox.Oauth key: @oauth.key + '0', secret: @oauth.secret
+ expect(oauth.appHash()).not.to.equal @oauth.appHash()
+ expect(oauth.appHash()).to.be.a 'string'
+ expect(oauth.appHash().length).to.be.greaterThan 4
+
+ describe '#constructor', ->
+ it 'raises an Error if initialized without an API key / secret', ->
+ expect(-> new Dropbox.Oauth(token: '123', tokenSecret: '456')).to.
+ throw(Error, /no api key/i)
+
diff --git a/lib/client/storage/dropbox/test/src/pulled_changes_test.coffee b/lib/client/storage/dropbox/test/src/pulled_changes_test.coffee
new file mode 100644
index 00000000..4d2fe67d
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/pulled_changes_test.coffee
@@ -0,0 +1,128 @@
+describe 'Dropbox.PulledChanges', ->
+ describe '.parse', ->
+ describe 'on a sample response', ->
+ beforeEach ->
+ deltaInfo = {
+ "reset": false,
+ "cursor": "nTZYLOcTQnyB7-Wc72M-kEAcBQdk2EjLaJIRupQWgDXmRwKWzuG5V4se2mvU7yzXn4cZSJltoW4tpbqgy0Ezxh1b1p3ygp7wy-vdaYJusujnLAyEsKdYCHPZYZdZt7sQG0BopF2ufAuD56ijYbdX5DhMKe85MFqncnFDvNxSjsodEw-IkCfNZmagDmpOZCxmLqu71hLTApwhqO9-dhm-fk6KSYs-OZwRmVwOE2JAnJbWuifNiM8KwMz5sRBZ5FMJPDqXpOW5PqPCwbkAmKQACbNXFi0k1JuxulpDlQh3zMr3lyLMs-fmaDTTU355mY5xSAXK05Zgs5rPJ6lcaBOUmEBSXcPhxFDHk5NmAdA03Shq04t2_4bupzWX-txT84FmOLNncchl7ZDBCMwyrAzD2kCYOTu1_lhui0C-fiCZgZBKU4OyP6qrkdo4gZu3",
+ "has_more": true,
+ "entries": [
+ [
+ "/Getting_Started.pdf",
+ {
+ "size": "225.4KB",
+ "rev": "35e97029684fe",
+ "thumb_exists": true, # Changed to test hasThumbnail=true code.
+ "bytes": 230783,
+ "modified": "Tue, 19 Jul 2011 21:55:38 +0000",
+ "client_mtime": "Mon, 18 Jul 2011 18:04:35 +0000",
+ "path": "/Getting_Started.pdf",
+ "is_dir": false,
+ "icon": "page_white_acrobat",
+ "root": "app_folder", # Changed to test app_folder code path.
+ "mime_type": "application/pdf",
+ "revision": 220823
+ }
+ ],
+ [
+ "/Public",
+ null
+ ]
+ ]
+ }
+ @changes = Dropbox.PulledChanges.parse deltaInfo
+
+ it 'parses blankSlate correctly', ->
+ expect(@changes).to.have.property 'blankSlate'
+ expect(@changes.blankSlate).to.equal false
+
+ it 'parses cursorTag correctly', ->
+ expect(@changes).to.have.property 'cursorTag'
+ expect(@changes.cursorTag).to.equal 'nTZYLOcTQnyB7-Wc72M-kEAcBQdk2EjLaJIRupQWgDXmRwKWzuG5V4se2mvU7yzXn4cZSJltoW4tpbqgy0Ezxh1b1p3ygp7wy-vdaYJusujnLAyEsKdYCHPZYZdZt7sQG0BopF2ufAuD56ijYbdX5DhMKe85MFqncnFDvNxSjsodEw-IkCfNZmagDmpOZCxmLqu71hLTApwhqO9-dhm-fk6KSYs-OZwRmVwOE2JAnJbWuifNiM8KwMz5sRBZ5FMJPDqXpOW5PqPCwbkAmKQACbNXFi0k1JuxulpDlQh3zMr3lyLMs-fmaDTTU355mY5xSAXK05Zgs5rPJ6lcaBOUmEBSXcPhxFDHk5NmAdA03Shq04t2_4bupzWX-txT84FmOLNncchl7ZDBCMwyrAzD2kCYOTu1_lhui0C-fiCZgZBKU4OyP6qrkdo4gZu3'
+
+ it 'parses shouldPullAgain correctly', ->
+ expect(@changes).to.have.property 'shouldPullAgain'
+ expect(@changes.shouldPullAgain).to.equal true
+
+ it 'parses shouldBackOff correctly', ->
+ expect(@changes).to.have.property 'shouldBackOff'
+ expect(@changes.shouldBackOff).to.equal false
+
+ it 'parses changes correctly', ->
+ expect(@changes).to.have.property 'changes'
+ expect(@changes.changes).to.have.length 2
+ expect(@changes.changes[0]).to.be.instanceOf Dropbox.PullChange
+ expect(@changes.changes[0].path).to.equal '/Getting_Started.pdf'
+ expect(@changes.changes[1]).to.be.instanceOf Dropbox.PullChange
+ expect(@changes.changes[1].path).to.equal '/Public'
+
+ it 'passes null through', ->
+ expect(Dropbox.PulledChanges.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.PulledChanges.parse(undefined)).to.equal undefined
+
+
+describe 'Dropbox.PullChange', ->
+ describe '.parse', ->
+ describe 'on a modification change', ->
+ beforeEach ->
+ entry = [
+ "/Getting_Started.pdf",
+ {
+ "size": "225.4KB",
+ "rev": "35e97029684fe",
+ "thumb_exists": true, # Changed to test hasThumbnail=true code.
+ "bytes": 230783,
+ "modified": "Tue, 19 Jul 2011 21:55:38 +0000",
+ "client_mtime": "Mon, 18 Jul 2011 18:04:35 +0000",
+ "path": "/Getting_Started.pdf",
+ "is_dir": false,
+ "icon": "page_white_acrobat",
+ "root": "app_folder", # Changed to test app_folder code path.
+ "mime_type": "application/pdf",
+ "revision": 220823
+ }
+ ]
+ @changes = Dropbox.PullChange.parse entry
+
+ it 'parses path correctly', ->
+ expect(@changes).to.have.property 'path'
+ expect(@changes.path).to.equal '/Getting_Started.pdf'
+
+ it 'parses wasRemoved correctly', ->
+ expect(@changes).to.have.property 'wasRemoved'
+ expect(@changes.wasRemoved).to.equal false
+
+ it 'parses stat correctly', ->
+ expect(@changes).to.have.property 'stat'
+ expect(@changes.stat).to.be.instanceOf Dropbox.Stat
+ expect(@changes.stat.path).to.equal @changes.path
+
+ describe 'on a deletion change', ->
+ beforeEach ->
+ entry = [
+ "/Public",
+ null
+ ]
+ @changes = Dropbox.PullChange.parse entry
+
+ it 'parses path correctly', ->
+ expect(@changes).to.have.property 'path'
+ expect(@changes.path).to.equal '/Public'
+
+ it 'parses wasRemoved correctly', ->
+ expect(@changes).to.have.property 'wasRemoved'
+ expect(@changes.wasRemoved).to.equal true
+
+ it 'parses stat correctly', ->
+ expect(@changes).to.have.property 'stat'
+ expect(@changes.stat).to.equal null
+
+ it 'passes null through', ->
+ expect(Dropbox.PullChange.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.PullChange.parse(undefined)).to.equal undefined
+
+
diff --git a/lib/client/storage/dropbox/test/src/references_test.coffee b/lib/client/storage/dropbox/test/src/references_test.coffee
new file mode 100644
index 00000000..ac03c567
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/references_test.coffee
@@ -0,0 +1,92 @@
+describe 'Dropbox.PublicUrl', ->
+ describe '.parse', ->
+ describe 'on the /shares API example', ->
+ beforeEach ->
+ urlData = {
+ "url": "http://db.tt/APqhX1",
+ "expires": "Tue, 01 Jan 2030 00:00:00 +0000"
+ }
+ @url = Dropbox.PublicUrl.parse urlData, false
+
+ it 'parses url correctly', ->
+ expect(@url).to.have.property 'url'
+ expect(@url.url).to.equal 'http://db.tt/APqhX1'
+
+ it 'parses expiresAt correctly', ->
+ expect(@url).to.have.property 'expiresAt'
+ expect(@url.expiresAt).to.be.instanceOf Date
+ expect([
+ 'Tue, 01 Jan 2030 00:00:00 GMT', # every sane JS platform
+ 'Tue, 1 Jan 2030 00:00:00 UTC' # Internet Explorer
+ ]).to.contain(@url.expiresAt.toUTCString())
+
+ it 'parses isDirect correctly', ->
+ expect(@url).to.have.property 'isDirect'
+ expect(@url.isDirect).to.equal false
+
+ it 'parses isPreview correctly', ->
+ expect(@url).to.have.property 'isPreview'
+ expect(@url.isPreview).to.equal true
+
+ it 'round-trips through json / parse correctly', ->
+ newUrl = Dropbox.PublicUrl.parse @url.json()
+ newUrl.json() # Get _json populated for newUrl.
+ expect(newUrl).to.deep.equal @url
+
+ it 'passes null through', ->
+ expect(Dropbox.PublicUrl.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.PublicUrl.parse(undefined)).to.equal undefined
+
+
+describe 'Dropbox.CopyReference', ->
+ describe '.parse', ->
+ describe 'on the API example', ->
+ beforeEach ->
+ refData = {
+ "copy_ref": "z1X6ATl6aWtzOGq0c3g5Ng",
+ "expires": "Fri, 31 Jan 2042 21:01:05 +0000"
+ }
+ @ref = Dropbox.CopyReference.parse refData
+
+ it 'parses tag correctly', ->
+ expect(@ref).to.have.property 'tag'
+ expect(@ref.tag).to.equal 'z1X6ATl6aWtzOGq0c3g5Ng'
+
+ it 'parses expiresAt correctly', ->
+ expect(@ref).to.have.property 'expiresAt'
+ expect(@ref.expiresAt).to.be.instanceOf Date
+ expect([
+ 'Fri, 31 Jan 2042 21:01:05 GMT', # every sane JS platform
+ 'Fri, 31 Jan 2042 21:01:05 UTC' # Internet Explorer
+ ]).to.contain(@ref.expiresAt.toUTCString())
+
+ it 'round-trips through json / parse correctly', ->
+ newRef = Dropbox.CopyReference.parse @ref.json()
+ expect(newRef).to.deep.equal @ref
+
+ describe 'on a reference string', ->
+ beforeEach ->
+ rawRef = 'z1X6ATl6aWtzOGq0c3g5Ng'
+ @ref = Dropbox.CopyReference.parse rawRef
+
+ it 'parses tag correctly', ->
+ expect(@ref).to.have.property 'tag'
+ expect(@ref.tag).to.equal 'z1X6ATl6aWtzOGq0c3g5Ng'
+
+ it 'parses expiresAt correctly', ->
+ expect(@ref).to.have.property 'expiresAt'
+ expect(@ref.expiresAt).to.be.instanceOf Date
+ expect(@ref.expiresAt - (new Date())).to.be.below 1000
+
+ it 'round-trips through json / parse correctly', ->
+ newRef = Dropbox.CopyReference.parse @ref.json()
+ expect(newRef).to.deep.equal @ref
+
+ it 'passes null through', ->
+ expect(Dropbox.CopyReference.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.CopyReference.parse(undefined)).to.equal undefined
+
diff --git a/lib/client/storage/dropbox/test/src/stat_test.coffee b/lib/client/storage/dropbox/test/src/stat_test.coffee
new file mode 100644
index 00000000..2fecf635
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/stat_test.coffee
@@ -0,0 +1,204 @@
+describe 'Dropbox.Stat', ->
+ describe '.parse', ->
+ describe 'on the API file example', ->
+ beforeEach ->
+ # File example at
+ # https://www.dropbox.com/developers/reference/api#metadata
+ metadata = {
+ "size": "225.4KB",
+ "rev": "35e97029684fe",
+ "thumb_exists": true, # Changed to test hasThumbnail=true code.
+ "bytes": 230783,
+ "modified": "Tue, 19 Jul 2011 21:55:38 +0000",
+ "client_mtime": "Mon, 18 Jul 2011 18:04:35 +0000",
+ "path": "/Getting_Started.pdf",
+ "is_dir": false,
+ "icon": "page_white_acrobat",
+ "root": "app_folder", # Changed to test app_folder code path.
+ "mime_type": "application/pdf",
+ "revision": 220823
+ }
+ @stat = Dropbox.Stat.parse metadata
+
+ it 'parses the path correctly', ->
+ expect(@stat).to.have.property 'path'
+ expect(@stat.path).to.equal '/Getting_Started.pdf'
+
+ it 'parses name correctly', ->
+ expect(@stat).to.have.property 'name'
+ expect(@stat.name).to.equal 'Getting_Started.pdf'
+
+ it 'parses inAppFolder corectly', ->
+ expect(@stat).to.have.property 'inAppFolder'
+ expect(@stat.inAppFolder).to.equal true
+
+ it 'parses isFolder correctly', ->
+ expect(@stat).to.have.property 'isFolder'
+ expect(@stat.isFolder).to.equal false
+ expect(@stat).to.have.property 'isFile'
+ expect(@stat.isFile).to.equal true
+
+ it 'parses isRemoved correctly', ->
+ expect(@stat).to.have.property 'isRemoved'
+ expect(@stat.isRemoved).to.equal false
+
+ it 'parses typeIcon correctly', ->
+ expect(@stat).to.have.property 'typeIcon'
+ expect(@stat.typeIcon).to.equal 'page_white_acrobat'
+
+ it 'parses versionTag correctly', ->
+ expect(@stat).to.have.property 'versionTag'
+ expect(@stat.versionTag).to.equal '35e97029684fe'
+
+ it 'parses mimeType correctly', ->
+ expect(@stat).to.have.property 'mimeType'
+ expect(@stat.mimeType).to.equal 'application/pdf'
+
+ it 'parses size correctly', ->
+ expect(@stat).to.have.property 'size'
+ expect(@stat.size).to.equal 230783
+
+ it 'parses humanSize correctly', ->
+ expect(@stat).to.have.property 'humanSize'
+ expect(@stat.humanSize).to.equal "225.4KB"
+
+ it 'parses hasThumbnail correctly', ->
+ expect(@stat).to.have.property 'hasThumbnail'
+ expect(@stat.hasThumbnail).to.equal true
+
+ it 'parses modifiedAt correctly', ->
+ expect(@stat).to.have.property 'modifiedAt'
+ expect(@stat.modifiedAt).to.be.instanceOf Date
+ expect([
+ 'Tue, 19 Jul 2011 21:55:38 GMT', # every sane JS platform
+ 'Tue, 19 Jul 2011 21:55:38 UTC' # Internet Explorer
+ ]).to.contain(@stat.modifiedAt.toUTCString())
+
+ it 'parses clientModifiedAt correctly', ->
+ expect(@stat).to.have.property 'clientModifiedAt'
+ expect(@stat.clientModifiedAt).to.be.instanceOf Date
+ expect([
+ 'Mon, 18 Jul 2011 18:04:35 GMT', # every sane JS platform
+ 'Mon, 18 Jul 2011 18:04:35 UTC' # Internet Explorer
+ ]).to.contain(@stat.clientModifiedAt.toUTCString())
+
+ it 'round-trips through json / parse correctly', ->
+ newStat = Dropbox.Stat.parse @stat.json()
+ expect(newStat).to.deep.equal @stat
+
+
+ describe 'on the API directory example', ->
+ beforeEach ->
+ # Folder example at
+ # https://www.dropbox.com/developers/reference/api#metadata
+ metadata = {
+ "size": "0 bytes",
+ "hash": "37eb1ba1849d4b0fb0b28caf7ef3af52",
+ "bytes": 0,
+ "thumb_exists": false,
+ "rev": "714f029684fe",
+ "modified": "Wed, 27 Apr 2011 22:18:51 +0000",
+ "path": "/Public",
+ "is_dir": true,
+ "is_deleted": true, # Added to test isRemoved=true code path.
+ "icon": "folder_public",
+ "root": "dropbox",
+ "revision": 29007
+ }
+ @stat = Dropbox.Stat.parse metadata
+
+ it 'parses path correctly', ->
+ expect(@stat).to.have.property 'path'
+ expect(@stat.path).to.equal '/Public'
+
+ it 'parses name correctly', ->
+ expect(@stat).to.have.property 'name'
+ expect(@stat.name).to.equal 'Public'
+
+ it 'parses inAppFolder corectly', ->
+ expect(@stat).to.have.property 'inAppFolder'
+ expect(@stat.inAppFolder).to.equal false
+
+ it 'parses isFolder correctly', ->
+ expect(@stat).to.have.property 'isFolder'
+ expect(@stat.isFolder).to.equal true
+ expect(@stat).to.have.property 'isFile'
+ expect(@stat.isFile).to.equal false
+
+ it 'parses isRemoved correctly', ->
+ expect(@stat).to.have.property 'isRemoved'
+ expect(@stat.isRemoved).to.equal true
+
+ it 'parses typeIcon correctly', ->
+ expect(@stat).to.have.property 'typeIcon'
+ expect(@stat.typeIcon).to.equal 'folder_public'
+
+ it 'parses versionTag correctly', ->
+ expect(@stat).to.have.property 'versionTag'
+ expect(@stat.versionTag).to.equal '37eb1ba1849d4b0fb0b28caf7ef3af52'
+
+ it 'parses mimeType correctly', ->
+ expect(@stat).to.have.property 'mimeType'
+ expect(@stat.mimeType).to.equal 'inode/directory'
+
+ it 'parses size correctly', ->
+ expect(@stat).to.have.property 'size'
+ expect(@stat.size).to.equal 0
+
+ it 'parses humanSize correctly', ->
+ expect(@stat).to.have.property 'humanSize'
+ expect(@stat.humanSize).to.equal '0 bytes'
+
+ it 'parses hasThumbnail correctly', ->
+ expect(@stat).to.have.property 'hasThumbnail'
+ expect(@stat.hasThumbnail).to.equal false
+
+ it 'parses modifiedAt correctly', ->
+ expect(@stat).to.have.property 'modifiedAt'
+ expect(@stat.modifiedAt).to.be.instanceOf Date
+ expect([
+ 'Wed, 27 Apr 2011 22:18:51 GMT', # every sane JS platform
+ 'Wed, 27 Apr 2011 22:18:51 UTC' # Internet Explorer
+ ]).to.contain(@stat.modifiedAt.toUTCString())
+
+ it 'parses missing clientModifiedAt correctly', ->
+ expect(@stat).to.have.property 'clientModifiedAt'
+ expect(@stat.clientModifiedAt).to.equal null
+
+ it 'round-trips through json / parse correctly', ->
+ newStat = Dropbox.Stat.parse @stat.json()
+ expect(newStat).to.deep.equal @stat
+
+ it 'passes null through', ->
+ expect(Dropbox.Stat.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.Stat.parse(undefined)).to.equal undefined
+
+ describe 'on a contrived file/path example', ->
+ beforeEach ->
+ metadata = {
+ "size": "225.4KB",
+ "rev": "35e97029684fe",
+ "thumb_exists": true, # Changed to test hasThumbnail=true code.
+ "bytes": 230783,
+ "modified": "Tue, 19 Jul 2011 21:55:38 +0000",
+ "client_mtime": "Mon, 18 Jul 2011 18:04:35 +0000",
+ "path": "path/to/a/file/named/Getting_Started.pdf/",
+ "is_dir": false,
+ "icon": "page_white_acrobat",
+ "root": "app_folder", # Changed to test app_folder code path.
+ "mime_type": "application/pdf",
+ "revision": 220823
+ }
+ @stat = Dropbox.Stat.parse metadata
+
+ it 'parses the path correctly', ->
+ expect(@stat).to.have.property 'path'
+ expect(@stat.path).to.equal '/path/to/a/file/named/Getting_Started.pdf'
+
+ it 'parses name correctly', ->
+ expect(@stat).to.have.property 'name'
+ expect(@stat.name).to.equal 'Getting_Started.pdf'
+
+
diff --git a/lib/client/storage/dropbox/test/src/token_stash.coffee b/lib/client/storage/dropbox/test/src/token_stash.coffee
new file mode 100644
index 00000000..8dec2440
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/token_stash.coffee
@@ -0,0 +1,105 @@
+# Stashes Dropbox access credentials.
+class TokenStash
+ # @param {Object} options the advanced options below
+ # @option options {Boolean} fullDropbox if true, the returned credentials
+ # will be good for full Dropbox access; otherwise, the credentials will
+ # work for Folder access
+ constructor: (options) ->
+ @fs = require 'fs'
+ # Node 0.6 hack.
+ unless @fs.existsSync
+ path = require 'path'
+ @fs.existsSync = (filePath) -> path.existsSync filePath
+ @getCache = null
+ @sandbox = !options?.fullDropbox
+ @setupFs()
+
+ # Calls the supplied method with the Dropbox access credentials.
+ get: (callback) ->
+ @getCache or= @readStash()
+ if @getCache
+ callback @getCache
+ return null
+
+ @liveLogin (fullCredentials, sandboxCredentials) =>
+ unless fullCredentials and sandboxCredentials
+ throw new Error('Dropbox API authorization failed')
+
+ @writeStash fullCredentials, sandboxCredentials
+ @getCache = @readStash()
+ callback @getCache
+
+ # Obtains credentials by doing a login on the live site.
+ liveLogin: (callback) ->
+ Dropbox = require '../../lib/dropbox'
+ sandboxClient = new Dropbox.Client @clientOptions().sandbox
+ fullClient = new Dropbox.Client @clientOptions().full
+ @setupAuth()
+ sandboxClient.authDriver @authDriver
+ sandboxClient.authenticate (error, data) =>
+ if error
+ @killAuth()
+ callback null
+ return
+ fullClient.authDriver @authDriver
+ fullClient.authenticate (error, data) =>
+ @killAuth()
+ if error
+ callback null
+ return
+ credentials = @clientOptions()
+ callback fullClient.credentials(), sandboxClient.credentials()
+
+ # Returns the options used to create a Dropbox Client.
+ clientOptions: ->
+ {
+ sandbox:
+ sandbox: true
+ key: 'gWJAiHNbmDA=|MJ3xAk3nLeByuOckISnHib+h+1zTCG3TKTOEFvAAZw=='
+ full:
+ key: 'OYaTsqx6IXA=|z8mRqmTRoSdCqvkTvHTZq4ZZNxa7I5wM8X5E33IwCA=='
+ }
+
+ # Reads the file containing the access credentials, if it is available.
+ #
+ # @return {Object?} parsed access credentials, or null if they haven't been
+ # stashed
+ readStash: ->
+ unless @fs.existsSync @jsonPath
+ return null
+ stash = JSON.parse @fs.readFileSync @jsonPath
+ if @sandbox then stash.sandbox else stash.full
+
+ # Stashes the access credentials for future test use.
+ writeStash: (fullCredentials, sandboxCredentials) ->
+ json = JSON.stringify full: fullCredentials, sandbox: sandboxCredentials
+ @fs.writeFileSync @jsonPath, json
+
+ js = "window.testKeys = #{JSON.stringify sandboxCredentials};" +
+ "window.testFullDropboxKeys = #{JSON.stringify fullCredentials};"
+ @fs.writeFileSync @jsPath, js
+
+ # Sets up a node.js server-based authentication driver.
+ setupAuth: ->
+ return if @authDriver
+
+ Dropbox = require '../../lib/dropbox'
+ @authDriver = new Dropbox.Drivers.NodeServer
+
+ # Shuts down the node.js server behind the authentication server.
+ killAuth: ->
+ return unless @authDriver
+
+ @authDriver.closeServer()
+ @authDriver = null
+
+ # Sets up the directory structure for the credential stash.
+ setupFs: ->
+ @dirPath = 'test/.token'
+ @jsonPath = 'test/.token/token.json'
+ @jsPath = 'test/.token/token.js'
+
+ unless @fs.existsSync @dirPath
+ @fs.mkdirSync @dirPath
+
+module.exports = TokenStash
diff --git a/lib/client/storage/dropbox/test/src/upload_cursor_test.coffee b/lib/client/storage/dropbox/test/src/upload_cursor_test.coffee
new file mode 100644
index 00000000..6997bda5
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/upload_cursor_test.coffee
@@ -0,0 +1,114 @@
+describe 'Dropbox.UploadCursor', ->
+ describe '.parse', ->
+ describe 'on the API example', ->
+ beforeEach ->
+ cursorData = {
+ "upload_id": "v0k84B0AT9fYkfMUp0sBTA",
+ "offset": 31337,
+ "expires": "Tue, 19 Jul 2011 21:55:38 +0000"
+ }
+ @cursor = Dropbox.UploadCursor.parse cursorData
+
+ it 'parses tag correctly', ->
+ expect(@cursor).to.have.property 'tag'
+ expect(@cursor.tag).to.equal 'v0k84B0AT9fYkfMUp0sBTA'
+
+ it 'parses offset correctly', ->
+ expect(@cursor).to.have.property 'offset'
+ expect(@cursor.offset).to.equal 31337
+
+ it 'parses expiresAt correctly', ->
+ expect(@cursor).to.have.property 'expiresAt'
+ expect(@cursor.expiresAt).to.be.instanceOf Date
+ expect([
+ 'Tue, 19 Jul 2011 21:55:38 GMT', # every sane JS platform
+ 'Tue, 19 Jul 2011 21:55:38 UTC' # Internet Explorer
+ ]).to.contain(@cursor.expiresAt.toUTCString())
+
+ it 'round-trips through json / parse correctly', ->
+ newCursor = Dropbox.UploadCursor.parse @cursor.json()
+ expect(newCursor).to.deep.equal @cursor
+
+ describe 'on a reference string', ->
+ beforeEach ->
+ rawRef = 'v0k84B0AT9fYkfMUp0sBTA'
+ @cursor = Dropbox.UploadCursor.parse rawRef
+
+ it 'parses tag correctly', ->
+ expect(@cursor).to.have.property 'tag'
+ expect(@cursor.tag).to.equal 'v0k84B0AT9fYkfMUp0sBTA'
+
+ it 'parses offset correctly', ->
+ expect(@cursor).to.have.property 'offset'
+ expect(@cursor.offset).to.equal 0
+
+ it 'parses expiresAt correctly', ->
+ expect(@cursor).to.have.property 'expiresAt'
+ expect(@cursor.expiresAt).to.be.instanceOf Date
+ expect(@cursor.expiresAt - (new Date())).to.be.below 1000
+
+ it 'round-trips through json / parse correctly', ->
+ newCursor = Dropbox.UploadCursor.parse @cursor.json()
+ newCursor.json() # Get _json populated for newCursor.
+ expect(newCursor).to.deep.equal @cursor
+
+ it 'passes null through', ->
+ expect(Dropbox.CopyReference.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.CopyReference.parse(undefined)).to.equal undefined
+
+ describe '.constructor', ->
+ describe 'with no arguments', ->
+ beforeEach ->
+ @cursor = new Dropbox.UploadCursor
+
+ it 'sets up tag correctly', ->
+ expect(@cursor).to.have.property 'tag'
+ expect(@cursor.tag).to.equal null
+
+ it 'parses offset correctly', ->
+ expect(@cursor).to.have.property 'offset'
+ expect(@cursor.offset).to.equal 0
+
+ it 'parses expiresAt correctly', ->
+ expect(@cursor).to.have.property 'expiresAt'
+ expect(@cursor.expiresAt - (new Date())).to.be.below 1000
+
+ it 'round-trips through json / parse correctly', ->
+ newCursor = Dropbox.UploadCursor.parse @cursor.json()
+ newCursor.json() # Get _json populated for newCursor.
+ expect(newCursor).to.deep.equal @cursor
+
+ describe '.replace', ->
+ beforeEach ->
+ @cursor = new Dropbox.UploadCursor
+
+ describe 'on the API example', ->
+ beforeEach ->
+ cursorData = {
+ "upload_id": "v0k84B0AT9fYkfMUp0sBTA",
+ "offset": 31337,
+ "expires": "Tue, 19 Jul 2011 21:55:38 +0000"
+ }
+ @cursor.replace cursorData
+
+ it 'parses tag correctly', ->
+ expect(@cursor).to.have.property 'tag'
+ expect(@cursor.tag).to.equal 'v0k84B0AT9fYkfMUp0sBTA'
+
+ it 'parses offset correctly', ->
+ expect(@cursor).to.have.property 'offset'
+ expect(@cursor.offset).to.equal 31337
+
+ it 'parses expiresAt correctly', ->
+ expect(@cursor).to.have.property 'expiresAt'
+ expect(@cursor.expiresAt).to.be.instanceOf Date
+ expect([
+ 'Tue, 19 Jul 2011 21:55:38 GMT', # every sane JS platform
+ 'Tue, 19 Jul 2011 21:55:38 UTC' # Internet Explorer
+ ]).to.contain(@cursor.expiresAt.toUTCString())
+
+ it 'round-trips through json / parse correctly', ->
+ newCursor = Dropbox.UploadCursor.parse @cursor.json()
+ expect(newCursor).to.deep.equal @cursor
diff --git a/lib/client/storage/dropbox/test/src/user_info_test.coffee b/lib/client/storage/dropbox/test/src/user_info_test.coffee
new file mode 100644
index 00000000..65e09476
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/user_info_test.coffee
@@ -0,0 +1,95 @@
+describe 'Dropbox.UserInfo', ->
+ describe '.parse', ->
+ describe 'on the API example', ->
+ beforeEach ->
+ userData = {
+ "referral_link": "https://www.dropbox.com/referrals/r1a2n3d4m5s6t7",
+ "display_name": "John P. User",
+ "uid": 12345678,
+ "country": "US",
+ "quota_info": {
+ "shared": 253738410565,
+ "quota": 107374182400000,
+ "normal": 680031877871
+ },
+ "email": "johnpuser@company.com" # Added to reflect real responses.
+ }
+ @userInfo = Dropbox.UserInfo.parse userData
+
+ it 'parses name correctly', ->
+ expect(@userInfo).to.have.property 'name'
+ expect(@userInfo.name).to.equal 'John P. User'
+
+ it 'parses email correctly', ->
+ expect(@userInfo).to.have.property 'email'
+ expect(@userInfo.email).to.equal 'johnpuser@company.com'
+
+ it 'parses countryCode correctly', ->
+ expect(@userInfo).to.have.property 'countryCode'
+ expect(@userInfo.countryCode).to.equal 'US'
+
+ it 'parses uid correctly', ->
+ expect(@userInfo).to.have.property 'uid'
+ expect(@userInfo.uid).to.equal '12345678'
+
+ it 'parses referralUrl correctly', ->
+ expect(@userInfo).to.have.property 'referralUrl'
+ expect(@userInfo.referralUrl).to.
+ equal 'https://www.dropbox.com/referrals/r1a2n3d4m5s6t7'
+
+ it 'parses quota correctly', ->
+ expect(@userInfo).to.have.property 'quota'
+ expect(@userInfo.quota).to.equal 107374182400000
+
+ it 'parses usedQuota correctly', ->
+ expect(@userInfo).to.have.property 'usedQuota'
+ expect(@userInfo.usedQuota).to.equal 933770288436
+
+ it 'parses privateBytes correctly', ->
+ expect(@userInfo).to.have.property 'privateBytes'
+ expect(@userInfo.privateBytes).to.equal 680031877871
+
+ it 'parses sharedBytes correctly', ->
+ expect(@userInfo).to.have.property 'usedQuota'
+ expect(@userInfo.sharedBytes).to.equal 253738410565
+
+ it 'parses publicAppUrl correctly', ->
+ expect(@userInfo.publicAppUrl).to.equal null
+
+ it 'round-trips through json / parse correctly', ->
+ newInfo = Dropbox.UserInfo.parse @userInfo.json()
+ expect(newInfo).to.deep.equal @userInfo
+
+ it 'passes null through', ->
+ expect(Dropbox.UserInfo.parse(null)).to.equal null
+
+ it 'passes undefined through', ->
+ expect(Dropbox.UserInfo.parse(undefined)).to.equal undefined
+
+
+ describe 'on real data from a "public app folder" application', ->
+ beforeEach ->
+ userData = {
+ "referral_link": "https://www.dropbox.com/referrals/NTM1OTg4MTA5",
+ "display_name": "Victor Costan",
+ "uid": 87654321, # Anonymized.
+ "public_app_url": "https://dl-web.dropbox.com/spa/90vw6zlu4268jh4/",
+ "country": "US",
+ "quota_info": {
+ "shared": 6074393565,
+ "quota": 73201090560,
+ "normal": 4684642723
+ },
+ "email": "spam@gmail.com" # Anonymized.
+ }
+ @userInfo = Dropbox.UserInfo.parse userData
+
+ it 'parses publicAppUrl correctly', ->
+ expect(@userInfo.publicAppUrl).to.
+ equal 'https://dl-web.dropbox.com/spa/90vw6zlu4268jh4'
+
+ it 'round-trips through json / parse correctly', ->
+ newInfo = Dropbox.UserInfo.parse @userInfo.json()
+ expect(newInfo).to.deep.equal @userInfo
+
+
diff --git a/lib/client/storage/dropbox/test/src/web_file_server.coffee b/lib/client/storage/dropbox/test/src/web_file_server.coffee
new file mode 100644
index 00000000..9c9dfd24
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/web_file_server.coffee
@@ -0,0 +1,54 @@
+express = require 'express'
+fs = require 'fs'
+https = require 'https'
+open = require 'open'
+
+# Tiny express.js server for the Web files.
+class WebFileServer
+ # Starts up a HTTP server.
+ constructor: (@port = 8911) ->
+ @createApp()
+
+ # Opens the test URL in a browser.
+ openBrowser: (appName) ->
+ open @testUrl(), appName
+
+ # The URL that should be used to start the tests.
+ testUrl: ->
+ "https://localhost:#{@port}/test/html/browser_test.html"
+
+ # The server code.
+ createApp: ->
+ @app = express()
+ @app.get '/diediedie', (request, response) =>
+ if 'failed' of request.query
+ failed = parseInt request.query['failed']
+ else
+ failed = 1
+ total = parseInt request.query['total'] || 0
+ passed = total - failed
+ exitCode = if failed == 0 then 0 else 1
+ console.log "#{passed} passed, #{failed} failed"
+
+ response.header 'Content-Type', 'image/png'
+ response.header 'Content-Length', '0'
+ response.end ''
+ unless 'NO_EXIT' of process.env
+ @server.close()
+ process.exit exitCode
+
+ @app.use (request, response, next) ->
+ response.header 'Access-Control-Allow-Origin', '*'
+ response.header 'Access-Control-Allow-Methods', 'DELETE,GET,POST,PUT'
+ response.header 'Access-Control-Allow-Headers',
+ 'Content-Type, Authorization'
+ next()
+
+ @app.use express.static(fs.realpathSync(__dirname + '/../../'),
+ { hidden: true })
+ options = key: fs.readFileSync 'test/ssl/cert.pem'
+ options.cert = options.key
+ @server = https.createServer(options, @app)
+ @server.listen @port
+
+module.exports = new WebFileServer
diff --git a/lib/client/storage/dropbox/test/src/xhr_test.coffee b/lib/client/storage/dropbox/test/src/xhr_test.coffee
new file mode 100644
index 00000000..afe84a69
--- /dev/null
+++ b/lib/client/storage/dropbox/test/src/xhr_test.coffee
@@ -0,0 +1,644 @@
+describe 'Dropbox.Xhr', ->
+ beforeEach ->
+ @node_js = module? and module?.exports? and require?
+ @oauth = new Dropbox.Oauth testKeys
+
+ describe 'with a GET', ->
+ beforeEach ->
+ @xhr = new Dropbox.Xhr 'GET', 'https://request.url'
+
+ it 'initializes correctly', ->
+ expect(@xhr.isGet).to.equal true
+ expect(@xhr.method).to.equal 'GET'
+ expect(@xhr.url).to.equal 'https://request.url'
+ expect(@xhr.preflight).to.equal false
+
+ describe '#setHeader', ->
+ beforeEach ->
+ @xhr.setHeader 'Range', 'bytes=0-1000'
+
+ it 'adds a HTTP header header', ->
+ expect(@xhr.headers).to.have.property 'Range'
+ expect(@xhr.headers['Range']).to.equal 'bytes=0-1000'
+
+ it 'does not work twice for the same header', ->
+ expect(=> @xhr.setHeader('Range', 'bytes=0-1000')).to.throw Error
+
+ it 'flags the Xhr as needing preflight', ->
+ expect(@xhr.preflight).to.equal true
+
+ it 'rejects Content-Type', ->
+ expect(=> @xhr.setHeader('Content-Type', 'text/plain')).to.throw Error
+
+ describe '#setParams', ->
+ beforeEach ->
+ @xhr.setParams 'param 1': true, 'answer': 42
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ it 'does not work twice', ->
+ expect(=> @xhr.setParams 'answer': 43).to.throw Error
+
+ describe '#paramsToUrl', ->
+ beforeEach ->
+ @xhr.paramsToUrl()
+
+ it 'changes the url', ->
+ expect(@xhr.url).to.
+ equal 'https://request.url?answer=42¶m%201=true'
+
+ it 'sets params to null', ->
+ expect(@xhr.params).to.equal null
+
+ describe '#paramsToBody', ->
+ it 'throws an error', ->
+ expect(=> @xhr.paramsToBody()).to.throw Error
+
+ describe '#addOauthParams', ->
+ beforeEach ->
+ @xhr.addOauthParams @oauth
+
+ it 'keeps existing params', ->
+ expect(@xhr.params).to.have.property 'answer'
+ expect(@xhr.params.answer).to.equal 42
+
+ it 'adds an oauth_signature param', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+
+ it 'does not add an Authorization header', ->
+ expect(@xhr.headers).not.to.have.property 'Authorization'
+
+ it 'does not work twice', ->
+ expect(=> @xhr.addOauthParams()).to.throw Error
+
+ describe '#addOauthHeader', ->
+ beforeEach ->
+ @xhr.addOauthHeader @oauth
+
+ it 'keeps existing params', ->
+ expect(@xhr.params).to.have.property 'answer'
+ expect(@xhr.params.answer).to.equal 42
+
+ it 'does not add an oauth_signature param', ->
+ expect(@xhr.params).not.to.have.property 'oauth_signature'
+
+ it 'adds an Authorization header', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe '#addOauthParams without params', ->
+ beforeEach ->
+ @xhr.addOauthParams @oauth
+
+ it 'adds an oauth_signature param', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+
+ describe '#addOauthHeader without params', ->
+ beforeEach ->
+ @xhr.addOauthHeader @oauth
+
+ it 'adds an Authorization header', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe '#signWithOauth', ->
+ describe 'for a request that does not need preflight', ->
+ beforeEach ->
+ @xhr.signWithOauth @oauth
+
+ if Dropbox.Xhr.doesPreflight
+ it 'uses addOauthParams', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader in node.js', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe 'for a request that needs preflight', ->
+ beforeEach ->
+ @xhr.setHeader 'Range', 'bytes=0-1000'
+ @xhr.signWithOauth @oauth
+
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do HTTP headers.
+ it 'uses addOauthParams in IE', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe 'with cacheFriendly: true', ->
+ describe 'for a request that does not need preflight', ->
+ beforeEach ->
+ @xhr.signWithOauth @oauth, true
+
+ if Dropbox.Xhr.ieXdr
+ it 'uses addOauthParams in IE', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe 'for a request that needs preflight', ->
+ beforeEach ->
+ @xhr.setHeader 'Range', 'bytes=0-1000'
+ @xhr.signWithOauth @oauth, true
+
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do HTTP headers.
+ it 'uses addOauthParams in IE', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe '#setFileField', ->
+ it 'throws an error', ->
+ expect(=> @xhr.setFileField('file', 'filename.bin', 'File Data
',
+ 'text/html')).to.throw Error
+
+ describe '#setBody', ->
+ it 'throws an error', ->
+ expect(=> @xhr.setBody('body data')).to.throw Error
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ describe '#setResponseType', ->
+ beforeEach ->
+ @xhr.setResponseType 'b'
+
+ it 'changes responseType', ->
+ expect(@xhr.responseType).to.equal 'b'
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ describe '#prepare with params', ->
+ beforeEach ->
+ @xhr.setParams answer: 42
+ @xhr.prepare()
+
+ it 'creates the native xhr', ->
+ expect(typeof @xhr.xhr).to.equal 'object'
+
+ it 'opens the native xhr', ->
+ return if Dropbox.Xhr.ieXdr # IE's XDR doesn't do readyState.
+ expect(@xhr.xhr.readyState).to.equal 1
+
+ it 'pushes the params in the url', ->
+ expect(@xhr.url).to.equal 'https://request.url?answer=42'
+
+ describe 'with a POST', ->
+ beforeEach ->
+ @xhr = new Dropbox.Xhr 'POST', 'https://request.url'
+
+ it 'initializes correctly', ->
+ expect(@xhr.isGet).to.equal false
+ expect(@xhr.method).to.equal 'POST'
+ expect(@xhr.url).to.equal 'https://request.url'
+ expect(@xhr.preflight).to.equal false
+
+ describe '#setHeader', ->
+ beforeEach ->
+ @xhr.setHeader 'Range', 'bytes=0-1000'
+
+ it 'adds a HTTP header header', ->
+ expect(@xhr.headers).to.have.property 'Range'
+ expect(@xhr.headers['Range']).to.equal 'bytes=0-1000'
+
+ it 'does not work twice for the same header', ->
+ expect(=> @xhr.setHeader('Range', 'bytes=0-1000')).to.throw Error
+
+ it 'flags the Xhr as needing preflight', ->
+ expect(@xhr.preflight).to.equal true
+
+ it 'rejects Content-Type', ->
+ expect(=> @xhr.setHeader('Content-Type', 'text/plain')).to.throw Error
+
+ describe '#setParams', ->
+ beforeEach ->
+ @xhr.setParams 'param 1': true, 'answer': 42
+
+ it 'does not work twice', ->
+ expect(=> @xhr.setParams 'answer': 43).to.throw Error
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ describe '#paramsToUrl', ->
+ beforeEach ->
+ @xhr.paramsToUrl()
+
+ it 'changes the url', ->
+ expect(@xhr.url).to.
+ equal 'https://request.url?answer=42¶m%201=true'
+
+ it 'sets params to null', ->
+ expect(@xhr.params).to.equal null
+
+ it 'does not set the body', ->
+ expect(@xhr.body).to.equal null
+
+ describe '#paramsToBody', ->
+ beforeEach ->
+ @xhr.paramsToBody()
+
+ it 'url-encodes the params', ->
+ expect(@xhr.body).to.equal 'answer=42¶m%201=true'
+
+ it 'sets the Content-Type header', ->
+ expect(@xhr.headers).to.have.property 'Content-Type'
+ expect(@xhr.headers['Content-Type']).to.
+ equal 'application/x-www-form-urlencoded'
+
+ it 'does not change the url', ->
+ expect(@xhr.url).to.equal 'https://request.url'
+
+ it 'does not work twice', ->
+ @xhr.setParams answer: 43
+ expect(=> @xhr.paramsToBody()).to.throw Error
+
+ describe '#addOauthParams', ->
+ beforeEach ->
+ @xhr.addOauthParams @oauth
+
+ it 'keeps existing params', ->
+ expect(@xhr.params).to.have.property 'answer'
+ expect(@xhr.params.answer).to.equal 42
+
+ it 'adds an oauth_signature param', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+
+ it 'does not add an Authorization header', ->
+ expect(@xhr.headers).not.to.have.property 'Authorization'
+
+ it 'does not work twice', ->
+ expect(=> @xhr.addOauthParams()).to.throw Error
+
+ describe '#addOauthHeader', ->
+ beforeEach ->
+ @xhr.addOauthHeader @oauth
+
+ it 'keeps existing params', ->
+ expect(@xhr.params).to.have.property 'answer'
+ expect(@xhr.params.answer).to.equal 42
+
+ it 'does not add an oauth_signature param', ->
+ expect(@xhr.params).not.to.have.property 'oauth_signature'
+
+ it 'adds an Authorization header', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe '#addOauthParams without params', ->
+ beforeEach ->
+ @xhr.addOauthParams @oauth
+
+ it 'adds an oauth_signature param', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+
+ describe '#addOauthHeader without params', ->
+ beforeEach ->
+ @xhr.addOauthHeader @oauth
+
+ it 'adds an Authorization header', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe '#signWithOauth', ->
+ describe 'for a request that does not need preflight', ->
+ beforeEach ->
+ @xhr.signWithOauth @oauth
+
+ if Dropbox.Xhr.doesPreflight
+ it 'uses addOauthParams', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader in node.js', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe 'for a request that needs preflight', ->
+ beforeEach ->
+ @xhr.setHeader 'Range', 'bytes=0-1000'
+ @xhr.signWithOauth @oauth
+
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do HTTP headers.
+ it 'uses addOauthParams in IE', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe 'with cacheFriendly: true', ->
+ describe 'for a request that does not need preflight', ->
+ beforeEach ->
+ @xhr.signWithOauth @oauth, true
+
+ if Dropbox.Xhr.doesPreflight
+ it 'uses addOauthParams', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader in node.js', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe 'for a request that needs preflight', ->
+ beforeEach ->
+ @xhr.setHeader 'Range', 'bytes=0-1000'
+ @xhr.signWithOauth @oauth, true
+
+ if Dropbox.Xhr.ieXdr # IE's XDR doesn't do HTTP headers.
+ it 'uses addOauthParams in IE', ->
+ expect(@xhr.params).to.have.property 'oauth_signature'
+ else
+ it 'uses addOauthHeader', ->
+ expect(@xhr.headers).to.have.property 'Authorization'
+
+ describe '#setFileField with a String', ->
+ beforeEach ->
+ @nonceStub = sinon.stub @xhr, 'multipartBoundary'
+ @nonceStub.returns 'multipart----boundary'
+ @xhr.setFileField 'file', 'filename.bin', 'File Data
',
+ 'text/html'
+
+ afterEach ->
+ @nonceStub.restore()
+
+ it 'sets the Content-Type header', ->
+ expect(@xhr.headers).to.have.property 'Content-Type'
+ expect(@xhr.headers['Content-Type']).to.
+ equal 'multipart/form-data; boundary=multipart----boundary'
+
+ it 'sets the body', ->
+ expect(@xhr.body).to.equal("""--multipart----boundary\r
+Content-Disposition: form-data; name="file"; filename="filename.bin"\r
+Content-Type: text/html\r
+Content-Transfer-Encoding: binary\r
+\r
+File Data
\r
+--multipart----boundary--\r\n
+""")
+
+ it 'does not work twice', ->
+ expect(=> @xhr.setFileField('file', 'filename.bin', 'File Data
',
+ 'text/html')).to.throw Error
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ describe '#setBody with a string', ->
+ beforeEach ->
+ @xhr.setBody 'body data'
+
+ it 'sets the request body', ->
+ expect(@xhr.body).to.equal 'body data'
+
+ it 'does not work twice', ->
+ expect(=> @xhr.setBody('body data')).to.throw Error
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ describe '#setBody with FormData', ->
+ beforeEach ->
+ if FormData?
+ formData = new FormData()
+ formData.append 'name', 'value'
+ @xhr.setBody formData
+
+ it 'does not flag the XHR as needing preflight', ->
+ return unless FormData?
+ expect(@xhr.preflight).to.equal false
+
+ describe '#setBody with Blob', ->
+ beforeEach ->
+ if Blob?
+ blob = new Blob ["abcdef"], type: 'image/png'
+ @xhr.setBody blob
+
+ it 'flags the XHR as needing preflight', ->
+ return unless Blob?
+ expect(@xhr.preflight).to.equal true
+
+ it 'sets the Content-Type header', ->
+ return unless Blob?
+ expect(@xhr.headers).to.have.property 'Content-Type'
+ expect(@xhr.headers['Content-Type']).to.
+ equal 'application/octet-stream'
+
+ describe '#setBody with ArrayBuffer', ->
+ beforeEach ->
+ if ArrayBuffer?
+ buffer = new ArrayBuffer 5
+ @xhr.setBody buffer
+
+ it 'flags the XHR as needing preflight', ->
+ return unless ArrayBuffer?
+ expect(@xhr.preflight).to.equal true
+
+ it 'sets the Content-Type header', ->
+ return unless ArrayBuffer?
+ expect(@xhr.headers).to.have.property 'Content-Type'
+ expect(@xhr.headers['Content-Type']).to.
+ equal 'application/octet-stream'
+
+ describe '#setBody with ArrayBufferView', ->
+ beforeEach ->
+ if Uint8Array?
+ view = new Uint8Array 5
+ @xhr.setBody view
+
+ it 'flags the XHR as needing preflight', ->
+ return unless Uint8Array?
+ expect(@xhr.preflight).to.equal true
+
+ it 'sets the Content-Type header', ->
+ return unless Uint8Array?
+ expect(@xhr.headers).to.have.property 'Content-Type'
+ expect(@xhr.headers['Content-Type']).to.
+ equal 'application/octet-stream'
+
+ describe '#setResponseType', ->
+ beforeEach ->
+ @xhr.setResponseType 'b'
+
+ it 'changes responseType', ->
+ expect(@xhr.responseType).to.equal 'b'
+
+ it 'does not flag the XHR as needing preflight', ->
+ expect(@xhr.preflight).to.equal false
+
+ describe '#prepare with params', ->
+ beforeEach ->
+ @xhr.setParams answer: 42
+ @xhr.prepare()
+
+ it 'creates the native xhr', ->
+ expect(typeof @xhr.xhr).to.equal 'object'
+
+ it 'opens the native xhr', ->
+ return if Dropbox.Xhr.ieXdr # IE's XDR doesn't do readyState.
+ expect(@xhr.xhr.readyState).to.equal 1
+
+ if Dropbox.Xhr.ieXdr
+ it 'keeps the params in the URL in IE', ->
+ expect(@xhr.url).to.equal 'https://request.url?answer=42'
+ expect(@xhr.body).to.equal null
+ else
+ it 'pushes the params in the body', ->
+ expect(@xhr.body).to.equal 'answer=42'
+
+ describe 'with a PUT', ->
+ beforeEach ->
+ @xhr = new Dropbox.Xhr 'PUT', 'https://request.url'
+
+ it 'initializes correctly', ->
+ expect(@xhr.isGet).to.equal false
+ expect(@xhr.method).to.equal 'PUT'
+ expect(@xhr.url).to.equal 'https://request.url'
+ expect(@xhr.preflight).to.equal true
+
+ describe '#send', ->
+ it 'reports errors correctly', (done) ->
+ @url = 'https://api.dropbox.com/1/oauth/request_token'
+ @xhr = new Dropbox.Xhr 'POST', @url
+ @xhr.prepare().send (error, data) =>
+ expect(data).to.equal undefined
+ expect(error).to.be.instanceOf Dropbox.ApiError
+ expect(error).to.have.property 'url'
+ expect(error.url).to.equal @url
+ expect(error).to.have.property 'method'
+ expect(error.method).to.equal 'POST'
+ unless Dropbox.Xhr.ieXdr # IE's XDR doesn't do HTTP status codes.
+ expect(error).to.have.property 'status'
+ expect(error.status).to.equal 401 # Bad OAuth request.
+ expect(error).to.have.property 'responseText'
+ expect(error.responseText).to.be.a 'string'
+ unless Dropbox.Xhr.ieXdr # IE's XDR hides the HTTP body on error.
+ expect(error).to.have.property 'response'
+ expect(error.response).to.be.an 'object'
+ expect(error.toString()).to.match /^Dropbox API error/
+ expect(error.toString()).to.contain 'POST'
+ expect(error.toString()).to.contain @url
+ done()
+
+ it 'reports errors correctly when onError is set', (done) ->
+ @url = 'https://api.dropbox.com/1/oauth/request_token'
+ @xhr = new Dropbox.Xhr 'POST', @url
+ @xhr.onError = new Dropbox.EventSource
+ listenerError = null
+ @xhr.onError.addListener (error) -> listenerError = error
+ @xhr.prepare().send (error, data) =>
+ expect(data).to.equal undefined
+ expect(error).to.be.instanceOf Dropbox.ApiError
+ expect(error).to.have.property 'url'
+ expect(error.url).to.equal @url
+ expect(error).to.have.property 'method'
+ expect(error.method).to.equal 'POST'
+ expect(listenerError).to.equal error
+ done()
+
+ it 'processes data correctly', (done) ->
+ xhr = new Dropbox.Xhr 'POST',
+ 'https://api.dropbox.com/1/oauth/request_token',
+ xhr.addOauthParams @oauth
+ xhr.prepare().send (error, data) ->
+ expect(error).to.not.be.ok
+ expect(data).to.have.property 'oauth_token'
+ expect(data).to.have.property 'oauth_token_secret'
+ done()
+
+ it 'processes data correctly when using setCallback', (done) ->
+ xhr = new Dropbox.Xhr 'POST',
+ 'https://api.dropbox.com/1/oauth/request_token',
+ xhr.addOauthParams @oauth
+ xhr.setCallback (error, data) ->
+ expect(error).to.not.be.ok
+ expect(data).to.have.property 'oauth_token'
+ expect(data).to.have.property 'oauth_token_secret'
+ done()
+ xhr.prepare().send()
+
+ it 'sends Authorize headers correctly', (done) ->
+ return done() if Dropbox.Xhr.ieXdr # IE's XDR doesn't set headers.
+
+ xhr = new Dropbox.Xhr 'POST',
+ 'https://api.dropbox.com/1/oauth/request_token',
+ xhr.addOauthHeader @oauth
+ xhr.prepare().send (error, data) ->
+ expect(error).to.equal null
+ expect(data).to.have.property 'oauth_token'
+ expect(data).to.have.property 'oauth_token_secret'
+ done()
+
+ describe 'with a binary response', ->
+ beforeEach ->
+ testImageServerOn()
+ @xhr = new Dropbox.Xhr 'GET', testImageUrl
+
+ afterEach ->
+ testImageServerOff()
+
+ describe 'with responseType b', ->
+ beforeEach ->
+ @xhr.setResponseType 'b'
+
+ it 'retrieves a string where each character is a byte', (done) ->
+ @xhr.prepare().send (error, data) ->
+ expect(error).to.not.be.ok
+ expect(data).to.be.a 'string'
+ expect(data).to.equal testImageBytes
+ done()
+
+ describe 'with responseType arraybuffer', ->
+ beforeEach ->
+ @xhr.setResponseType 'arraybuffer'
+
+ it 'retrieves a well-formed ArrayBuffer', (done) ->
+ # Skip this test on node.js and IE 9 and below
+ return done() unless ArrayBuffer?
+
+ @xhr.prepare().send (error, buffer) ->
+ expect(error).to.not.be.ok
+ expect(buffer).to.be.instanceOf ArrayBuffer
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal testImageBytes
+ done()
+
+ describe 'with responseType blob', ->
+ beforeEach ->
+ @xhr.setResponseType 'blob'
+
+ it 'retrieves a well-formed Blob', (done) ->
+ # Skip this test on node.js and IE 9 and below
+ return done() unless Blob?
+
+ @xhr.prepare().send (error, blob) ->
+ expect(error).to.not.be.ok
+ expect(blob).to.be.instanceOf Blob
+ reader = new FileReader
+ reader.onloadend = ->
+ return unless reader.readyState == FileReader.DONE
+ buffer = reader.result
+ view = new Uint8Array buffer
+ length = buffer.byteLength
+ bytes = (String.fromCharCode view[i] for i in [0...length]).
+ join('')
+ expect(bytes).to.equal testImageBytes
+ done()
+ reader.readAsArrayBuffer blob
+
+ describe '#urlEncode', ->
+ it 'iterates properly', ->
+ expect(Dropbox.Xhr.urlEncode({foo: 'bar', baz: 5})).to.
+ equal 'baz=5&foo=bar'
+ it 'percent-encodes properly', ->
+ expect(Dropbox.Xhr.urlEncode({'a +x()': "*b'"})).to.
+ equal 'a%20%2Bx%28%29=%2Ab%27'
+
+ describe '#urlDecode', ->
+ it 'iterates properly', ->
+ decoded = Dropbox.Xhr.urlDecode('baz=5&foo=bar')
+ expect(decoded['baz']).to.equal '5'
+ expect(decoded['foo']).to.equal 'bar'
+ it 'percent-decodes properly', ->
+ decoded = Dropbox.Xhr.urlDecode('a%20%2Bx%28%29=%2Ab%27')
+ expect(decoded['a +x()']).to.equal "*b'"
+