Full Code of blueimp/jQuery-File-Upload for AI

master 0e92a4d4613d cached
52 files
1.2 MB
341.9k tokens
588 symbols
1 requests
Download .txt
Showing preview only (1,258K chars total). Download the full file or copy to clipboard to get everything.
Repository: blueimp/jQuery-File-Upload
Branch: master
Commit: 0e92a4d4613d
Files: 52
Total size: 1.2 MB

Directory structure:
gitextract_3s53bgjs/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── test.yml
├── .gitignore
├── LICENSE.txt
├── README.md
├── SECURITY.md
├── VULNERABILITIES.md
├── cors/
│   ├── postmessage.html
│   └── result.html
├── css/
│   ├── jquery.fileupload-noscript.css
│   ├── jquery.fileupload-ui-noscript.css
│   ├── jquery.fileupload-ui.css
│   └── jquery.fileupload.css
├── docker-compose.yml
├── index.html
├── js/
│   ├── cors/
│   │   ├── jquery.postmessage-transport.js
│   │   └── jquery.xdr-transport.js
│   ├── demo.js
│   ├── jquery.fileupload-audio.js
│   ├── jquery.fileupload-image.js
│   ├── jquery.fileupload-process.js
│   ├── jquery.fileupload-ui.js
│   ├── jquery.fileupload-validate.js
│   ├── jquery.fileupload-video.js
│   ├── jquery.fileupload.js
│   ├── jquery.iframe-transport.js
│   └── vendor/
│       └── jquery.ui.widget.js
├── package.json
├── server/
│   ├── gae-python/
│   │   ├── app.yaml
│   │   ├── main.py
│   │   └── static/
│   │       └── robots.txt
│   └── php/
│       ├── .dockerignore
│       ├── Dockerfile
│       ├── UploadHandler.php
│       ├── files/
│       │   ├── .gitignore
│       │   └── .htaccess
│       ├── index.php
│       └── php.ini
├── test/
│   ├── index.html
│   ├── unit.js
│   └── vendor/
│       ├── chai.js
│       ├── mocha.css
│       └── mocha.js
└── wdio/
    ├── .eslintrc.js
    ├── .prettierrc.js
    ├── LICENSE.txt
    ├── conf/
    │   ├── chrome.js
    │   └── firefox.js
    ├── hooks/
    │   └── index.js
    ├── test/
    │   ├── pages/
    │   │   └── file-upload.js
    │   └── specs/
    │       └── 01-file-upload.js
    └── wdio.conf.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: [blueimp]


================================================
FILE: .github/workflows/test.yml
================================================
name: Test

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14, 16]
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm install
      - run: npm run lint

  mocha:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: chmod
        run: chmod -R 777 server/php/files
      - name: docker-compose build
        run: docker-compose build example mocha
      - name: mocha
        run: docker-compose run --rm mocha
      - name: docker-compose logs
        if: always()
        run: docker-compose logs example
      - name: docker-compose down
        if: always()
        run: docker-compose down -v

  wdio-chrome:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: chmod
        run: chmod -R 777 server/php/files wdio/reports
      - name: docker-compose build
        run: docker-compose build example
      - name: wdio chrome
        run: docker-compose run --rm wdio
      - name: docker-compose logs
        if: always()
        run: docker-compose logs example
      - name: docker-compose down
        if: always()
        run: docker-compose down -v
      - name: Upload reports
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: reports
          path: wdio/reports

  wdio-firefox:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: chmod
        run: chmod -R 777 server/php/files wdio/reports
      - name: docker-compose build
        run: docker-compose build example
      - name: wdio firefox
        run: docker-compose run --rm wdio conf/firefox.js
      - name: docker-compose logs
        if: always()
        run: docker-compose logs example
      - name: docker-compose down
        if: always()
        run: docker-compose down -v
      - name: Upload reports
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: reports
          path: wdio/reports


================================================
FILE: .gitignore
================================================
*.pyc
.env
node_modules


================================================
FILE: LICENSE.txt
================================================
MIT License

Copyright © 2010 Sebastian Tschan, https://blueimp.net

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
# jQuery File Upload

## Contents

- [Description](#description)
- [Demo](#demo)
- [Features](#features)
- [Security](#security)
- [Setup](#setup)
- [Requirements](#requirements)
  - [Mandatory requirements](#mandatory-requirements)
  - [Optional requirements](#optional-requirements)
  - [Cross-domain requirements](#cross-domain-requirements)
- [Browsers](#browsers)
  - [Desktop browsers](#desktop-browsers)
  - [Mobile browsers](#mobile-browsers)
  - [Extended browser support information](#extended-browser-support-information)
- [Testing](#testing)
- [Support](#support)
- [License](#license)

## Description

> File Upload widget with multiple file selection, drag&drop support, progress
> bars, validation and preview images, audio and video for jQuery.  
> Supports cross-domain, chunked and resumable file uploads and client-side
> image resizing.  
> Works with any server-side platform (PHP, Python, Ruby on Rails, Java,
> Node.js, Go etc.) that supports standard HTML form file uploads.

## Demo

[Demo File Upload](https://blueimp.github.io/jQuery-File-Upload/)

## Features

- **Multiple file upload:**  
  Allows to select multiple files at once and upload them simultaneously.
- **Drag & Drop support:**  
  Allows to upload files by dragging them from your desktop or file manager and
  dropping them on your browser window.
- **Upload progress bar:**  
  Shows a progress bar indicating the upload progress for individual files and
  for all uploads combined.
- **Cancelable uploads:**  
  Individual file uploads can be canceled to stop the upload progress.
- **Resumable uploads:**  
  Aborted uploads can be resumed with browsers supporting the Blob API.
- **Chunked uploads:**  
  Large files can be uploaded in smaller chunks with browsers supporting the
  Blob API.
- **Client-side image resizing:**  
  Images can be automatically resized on client-side with browsers supporting
  the required JS APIs.
- **Preview images, audio and video:**  
  A preview of image, audio and video files can be displayed before uploading
  with browsers supporting the required APIs.
- **No browser plugins (e.g. Adobe Flash) required:**  
  The implementation is based on open standards like HTML5 and JavaScript and
  requires no additional browser plugins.
- **Graceful fallback for legacy browsers:**  
  Uploads files via XMLHttpRequests if supported and uses iframes as fallback
  for legacy browsers.
- **HTML file upload form fallback:**  
  Allows progressive enhancement by using a standard HTML file upload form as
  widget element.
- **Cross-site file uploads:**  
  Supports uploading files to a different domain with cross-site XMLHttpRequests
  or iframe redirects.
- **Multiple plugin instances:**  
  Allows to use multiple plugin instances on the same webpage.
- **Customizable and extensible:**  
  Provides an API to set individual options and define callback methods for
  various upload events.
- **Multipart and file contents stream uploads:**  
  Files can be uploaded as standard "multipart/form-data" or file contents
  stream (HTTP PUT file upload).
- **Compatible with any server-side application platform:**  
  Works with any server-side platform (PHP, Python, Ruby on Rails, Java,
  Node.js, Go etc.) that supports standard HTML form file uploads.

## Security

⚠️ Please read the [VULNERABILITIES](VULNERABILITIES.md) document for a list of
fixed vulnerabilities

Please also read the [SECURITY](SECURITY.md) document for instructions on how to
securely configure your Web server for file uploads.

## Setup

jQuery File Upload can be installed via [NPM](https://www.npmjs.com/):

```sh
npm install blueimp-file-upload
```

This allows you to include [jquery.fileupload.js](js/jquery.fileupload.js) and
its extensions via `node_modules`, e.g:

```html
<script src="node_modules/blueimp-file-upload/js/jquery.fileupload.js"></script>
```

The widget can then be initialized on a file upload form the following way:

```js
$('#fileupload').fileupload();
```

For further information, please refer to the following guides:

- [Main documentation page](https://github.com/blueimp/jQuery-File-Upload/wiki)
- [List of all available Options](https://github.com/blueimp/jQuery-File-Upload/wiki/Options)
- [The plugin API](https://github.com/blueimp/jQuery-File-Upload/wiki/API)
- [How to setup the plugin on your website](https://github.com/blueimp/jQuery-File-Upload/wiki/Setup)
- [How to use only the basic plugin.](https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin)

## Requirements

### Mandatory requirements

- [jQuery](https://jquery.com/) v1.7+
- [jQuery UI widget factory](https://api.jqueryui.com/jQuery.widget/) v1.9+
  (included): Required for the basic File Upload plugin, but very lightweight
  without any other dependencies from the jQuery UI suite.
- [jQuery Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js)
  (included): Required for
  [browsers without XHR file upload support](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support).

### Optional requirements

- [JavaScript Templates engine](https://github.com/blueimp/JavaScript-Templates)
  v3+: Used to render the selected and uploaded files.
- [JavaScript Load Image library](https://github.com/blueimp/JavaScript-Load-Image)
  v2+: Required for the image previews and resizing functionality.
- [JavaScript Canvas to Blob polyfill](https://github.com/blueimp/JavaScript-Canvas-to-Blob)
  v3+:Required for the resizing functionality.
- [blueimp Gallery](https://github.com/blueimp/Gallery) v2+: Used to display the
  uploaded images in a lightbox.
- [Bootstrap](https://getbootstrap.com/) v3+: Used for the demo design.
- [Glyphicons](https://glyphicons.com/) Icon set used by Bootstrap.

### Cross-domain requirements

[Cross-domain File Uploads](https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads)
using the
[Iframe Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/jquery.iframe-transport.js)
require a redirect back to the origin server to retrieve the upload results. The
[example implementation](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/main.js)
makes use of
[result.html](https://github.com/blueimp/jQuery-File-Upload/blob/master/cors/result.html)
as a static redirect page for the origin server.

The repository also includes the
[jQuery XDomainRequest Transport plugin](https://github.com/blueimp/jQuery-File-Upload/blob/master/js/cors/jquery.xdr-transport.js),
which enables limited cross-domain AJAX requests in Microsoft Internet Explorer
8 and 9 (IE 10 supports cross-domain XHR requests).  
The XDomainRequest object allows GET and POST requests only and doesn't support
file uploads. It is used on the
[Demo](https://blueimp.github.io/jQuery-File-Upload/) to delete uploaded files
from the cross-domain demo file upload service.

## Browsers

### Desktop browsers

The File Upload plugin is regularly tested with the latest browser versions and
supports the following minimal versions:

- Google Chrome
- Apple Safari 4.0+
- Mozilla Firefox 3.0+
- Opera 11.0+
- Microsoft Internet Explorer 6.0+

### Mobile browsers

The File Upload plugin has been tested with and supports the following mobile
browsers:

- Apple Safari on iOS 6.0+
- Google Chrome on iOS 6.0+
- Google Chrome on Android 4.0+
- Default Browser on Android 2.3+
- Opera Mobile 12.0+

### Extended browser support information

For a detailed overview of the features supported by each browser version and
known operating system / browser bugs, please have a look at the
[Extended browser support information](https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support).

## Testing

The project comes with three sets of tests:

1. Code linting using [ESLint](https://eslint.org/).
2. Unit tests using [Mocha](https://mochajs.org/).
3. End-to-end tests using [blueimp/wdio](https://github.com/blueimp/wdio).

To run the tests, follow these steps:

1. Start [Docker](https://docs.docker.com/).
2. Install development dependencies:
   ```sh
   npm install
   ```
3. Run the tests:
   ```sh
   npm test
   ```

## Support

This project is actively maintained, but there is no official support channel.  
If you have a question that another developer might help you with, please post
to
[Stack Overflow](https://stackoverflow.com/questions/tagged/blueimp+jquery+file-upload)
and tag your question with `blueimp jquery file upload`.

## License

Released under the [MIT license](https://opensource.org/licenses/MIT).


================================================
FILE: SECURITY.md
================================================
# File Upload Security

## Contents

- [Introduction](#introduction)
- [Purpose of this project](#purpose-of-this-project)
- [Mitigations against file upload risks](#mitigations-against-file-upload-risks)
  - [Prevent code execution on the server](#prevent-code-execution-on-the-server)
  - [Prevent code execution in the browser](#prevent-code-execution-in-the-browser)
  - [Prevent distribution of malware](#prevent-distribution-of-malware)
- [Secure file upload serving configurations](#secure-file-upload-serving-configurations)
  - [Apache config](#apache-config)
  - [NGINX config](#nginx-config)
- [Secure image processing configurations](#secure-image-processing-configurations)
- [ImageMagick config](#imagemagick-config)

## Introduction

For an in-depth understanding of the potential security risks of providing file
uploads and possible mitigations, please refer to the
[OWASP - Unrestricted File Upload](https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload)
documentation.

To securely setup the project to serve uploaded files, please refer to the
sample
[Secure file upload serving configurations](#secure-file-upload-serving-configurations).

To mitigate potential vulnerabilities in image processing libraries, please
refer to the
[Secure image processing configurations](#secure-image-processing-configurations).

By default, all sample upload handlers allow only upload of image files, which
mitigates some attack vectors, but should not be relied on as the only
protection.

Please also have a look at the
[list of fixed vulnerabilities](VULNERABILITIES.md) in jQuery File Upload, which
relates mostly to the sample server-side upload handlers and how they have been
configured.

## Purpose of this project

Please note that this project is not a complete file management product, but
foremost a client-side file upload library for [jQuery](https://jquery.com/).  
The server-side sample upload handlers are just examples to demonstrate the
client-side file upload functionality.

To make this very clear, there is **no user authentication** by default:

- **everyone can upload files**
- **everyone can delete uploaded files**

In some cases this can be acceptable, but for most projects you will want to
extend the sample upload handlers to integrate user authentication, or implement
your own.

It is also up to you to configure your web server to securely serve the uploaded
files, e.g. using the
[sample server configurations](#secure-file-upload-serving-configurations).

## Mitigations against file upload risks

### Prevent code execution on the server

To prevent execution of scripts or binaries on server-side, the upload directory
must be configured to not execute files in the upload directory (e.g.
`server/php/files` as the default for the PHP upload handler) and only treat
uploaded files as static content.

The recommended way to do this is to configure the upload directory path to
point outside of the web application root.  
Then the web server can be configured to serve files from the upload directory
with their default static files handler only.

Limiting file uploads to a whitelist of safe file types (e.g. image files) also
mitigates this issue, but should not be the only protection.

### Prevent code execution in the browser

To prevent execution of scripts on client-side, the following headers must be
sent when delivering generic uploaded files to the client:

```
Content-Type: application/octet-stream
X-Content-Type-Options: nosniff
```

The `Content-Type: application/octet-stream` header instructs browsers to
display a download dialog instead of parsing it and possibly executing script
content e.g. in HTML files.

The `X-Content-Type-Options: nosniff` header prevents browsers to try to detect
the file mime type despite the given content-type header.

For known safe files, the content-type header can be adjusted using a
**whitelist**, e.g. sending `Content-Type: image/png` for PNG files.

### Prevent distribution of malware

To prevent attackers from uploading and distributing malware (e.g. computer
viruses), it is recommended to limit file uploads only to a whitelist of safe
file types.

Please note that the detection of file types in the sample file upload handlers
is based on the file extension and not the actual file content. This makes it
still possible for attackers to upload malware by giving their files an image
file extension, but should prevent automatic execution on client computers when
opening those files.

It does not protect at all from exploiting vulnerabilities in image display
programs, nor from users renaming file extensions to inadvertently execute the
contained malicious code.

## Secure file upload serving configurations

The following configurations serve uploaded files as static files with the
proper headers as
[mitigation against file upload risks](#mitigations-against-file-upload-risks).  
Please do not simply copy&paste these configurations, but make sure you
understand what they are doing and that you have implemented them correctly.

> Always test your own setup and make sure that it is secure!

e.g. try uploading PHP scripts (as "example.php", "example.php.png" and
"example.png") to see if they get executed by your web server, e.g. the content
of the following sample:

```php
GIF89ad <?php echo mime_content_type(__FILE__); phpinfo();
```

### Apache config

Add the following directive to the Apache config (e.g.
/etc/apache2/apache2.conf), replacing the directory path with the absolute path
to the upload directory:

```ApacheConf
<Directory "/path/to/project/server/php/files">
  # Some of the directives require the Apache Headers module. If it is not
  # already enabled, please execute the following command and reload Apache:
  # sudo a2enmod headers
  #
  # Please note that the order of directives across configuration files matters,
  # see also:
  # https://httpd.apache.org/docs/current/sections.html#merging

  # The following directive matches all files and forces them to be handled as
  # static content, which prevents the server from parsing and executing files
  # that are associated with a dynamic runtime, e.g. PHP files.
  # It also forces their Content-Type header to "application/octet-stream" and
  # adds a "Content-Disposition: attachment" header to force a download dialog,
  # which prevents browsers from interpreting files in the context of the
  # web server, e.g. HTML files containing JavaScript.
  # Lastly it also prevents browsers from MIME-sniffing the Content-Type,
  # preventing them from interpreting a file as a different Content-Type than
  # the one sent by the webserver.
  <FilesMatch ".*">
    SetHandler default-handler
    ForceType application/octet-stream
    Header set Content-Disposition attachment
    Header set X-Content-Type-Options nosniff
  </FilesMatch>

  # The following directive matches known image files and unsets the forced
  # Content-Type so they can be served with their original mime type.
  # It also unsets the Content-Disposition header to allow displaying them
  # inline in the browser.
  <FilesMatch ".+\.(?i:(gif|jpe?g|png))$">
    ForceType none
    Header unset Content-Disposition
  </FilesMatch>
</Directory>
```

### NGINX config

Add the following directive to the NGINX config, replacing the directory path
with the absolute path to the upload directory:

```Nginx
location ^~ /path/to/project/server/php/files {
    root html;
    default_type application/octet-stream;
    types {
        image/gif     gif;
        image/jpeg    jpg;
        image/png    png;
    }
    add_header X-Content-Type-Options 'nosniff';
    if ($request_filename ~ /(((?!\.(jpg)|(png)|(gif)$)[^/])+$)) {
        add_header Content-Disposition 'attachment; filename="$1"';
        # Add X-Content-Type-Options again, as using add_header in a new context
        # dismisses all previous add_header calls:
        add_header X-Content-Type-Options 'nosniff';
    }
}
```

## Secure image processing configurations

The following configuration mitigates
[potential image processing vulnerabilities with ImageMagick](VULNERABILITIES.md#potential-vulnerabilities-with-php-imagemagick)
by limiting the attack vectors to a small subset of image types
(`GIF/JPEG/PNG`).

Please also consider using alternative, safer image processing libraries like
[libvips](https://github.com/libvips/libvips) or
[imageflow](https://github.com/imazen/imageflow).

## ImageMagick config

It is recommended to disable all non-required ImageMagick coders via
[policy.xml](https://wiki.debian.org/imagemagick/security).  
To do so, locate the ImageMagick `policy.xml` configuration file and add the
following policies:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- ... -->
<policymap>
  <!-- ... -->
  <policy domain="delegate" rights="none" pattern="*" />
  <policy domain="coder" rights="none" pattern="*" />
  <policy domain="coder" rights="read | write" pattern="{GIF,JPEG,JPG,PNG}" />
</policymap>
```


================================================
FILE: VULNERABILITIES.md
================================================
# List of fixed vulnerabilities

## Contents

- [Potential vulnerabilities with PHP+ImageMagick](#potential-vulnerabilities-with-phpimagemagick)
- [Remote code execution vulnerability in the PHP component](#remote-code-execution-vulnerability-in-the-php-component)
- [Open redirect vulnerability in the GAE components](#open-redirect-vulnerability-in-the-gae-components)
- [Cross-site scripting vulnerability in the Iframe Transport](#cross-site-scripting-vulnerability-in-the-iframe-transport)

## Potential vulnerabilities with PHP+ImageMagick

> Mitigated: 2018-10-25 (GMT)

The sample [PHP upload handler](server/php/UploadHandler.php) before
[v9.25.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.25.1)
did not validate file signatures before invoking
[ImageMagick](https://www.imagemagick.org/) (via
[Imagick](https://php.net/manual/en/book.imagick.php)).  
Verifying those
[magic bytes](https://en.wikipedia.org/wiki/List_of_file_signatures) mitigates
potential vulnerabilities when handling input files other than `GIF/JPEG/PNG`.

Please also configure ImageMagick to only enable the coders required for
`GIF/JPEG/PNG` processing, e.g. with the sample
[ImageMagick config](SECURITY.md#imagemagick-config).

**Further information:**

- Commit containing the mitigation:
  [fe44d34](https://github.com/blueimp/jQuery-File-Upload/commit/fe44d34be43be32c6b8d507932f318dababb25dd)
- [ImageTragick](https://imagetragick.com/)
- [CERT Vulnerability Note VU#332928](https://www.kb.cert.org/vuls/id/332928)
- [ImageMagick CVE entries](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=imagemagick)

## Remote code execution vulnerability in the PHP component

> Fixed: 2018-10-23 (GMT)

The sample [PHP upload handler](server/php/UploadHandler.php) before
[v9.24.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/v9.24.1)
allowed to upload all file types by default.  
This opens up a remote code execution vulnerability, unless the server is
configured to not execute (PHP) files in the upload directory
(`server/php/files`).

The provided [.htaccess](server/php/files/.htaccess) file includes instructions
for Apache to disable script execution, however
[.htaccess support](https://httpd.apache.org/docs/current/howto/htaccess.html)
is disabled by default since Apache `v2.3.9` via
[AllowOverride Directive](https://httpd.apache.org/docs/current/mod/core.html#allowoverride).

**You are affected if you:**

1. A) Uploaded jQuery File Upload < `v9.24.1` on a Webserver that executes files
   with `.php` as part of the file extension (e.g. "example.php.png"), e.g.
   Apache with `mod_php` enabled and the following directive (_not a recommended
   configuration_):
   ```ApacheConf
   AddHandler php5-script .php
   ```
   B) Uploaded jQuery File Upload < `v9.22.1` on a Webserver that executes files
   with the file extension `.php`, e.g. Apache with `mod_php` enabled and the
   following directive:
   ```ApacheConf
   <FilesMatch \.php$>
     SetHandler application/x-httpd-php
   </FilesMatch>
   ```
2. Did not actively configure your Webserver to not execute files in the upload
   directory (`server/php/files`).
3. Are running Apache `v2.3.9+` with the default `AllowOverride` Directive set
   to `None` or another Webserver with no `.htaccess` support.

**How to fix it:**

1. Upgrade to the latest version of jQuery File Upload.
2. Configure your Webserver to not execute files in the upload directory, e.g.
   with the [sample Apache configuration](SECURITY.md#apache-config)

**Further information:**

- Commits containing the security fix:
  [aeb47e5](https://github.com/blueimp/jQuery-File-Upload/commit/aeb47e51c67df8a504b7726595576c1c66b5dc2f),
  [ad4aefd](https://github.com/blueimp/jQuery-File-Upload/commit/ad4aefd96e4056deab6fea2690f0d8cf56bb2d7d)
- [Full disclosure post on Hacker News](https://news.ycombinator.com/item?id=18267309).
- [CVE-2018-9206](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-9206)
- [OWASP - Unrestricted File Upload](https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload)

## Open redirect vulnerability in the GAE components

> Fixed: 2015-06-12 (GMT)

The sample Google App Engine upload handlers before
v[9.10.1](https://github.com/blueimp/jQuery-File-Upload/releases/tag/9.10.1)
accepted any URL as redirect target, making it possible to use the Webserver's
domain for phishing attacks.

**Further information:**

- Commit containing the security fix:
  [f74d2a8](https://github.com/blueimp/jQuery-File-Upload/commit/f74d2a8c3e3b1e8e336678d2899facd5bcdb589f)
- [OWASP - Unvalidated Redirects and Forwards Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html)

## Cross-site scripting vulnerability in the Iframe Transport

> Fixed: 2012-08-09 (GMT)

The [redirect page](cors/result.html) for the
[Iframe Transport](js/jquery.iframe-transport.js) before commit
[4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a)
(_fixed in all tagged releases_) allowed executing arbitrary JavaScript in the
context of the Webserver.

**Further information:**

- Commit containing the security fix:
  [4175032](https://github.com/blueimp/jQuery-File-Upload/commit/41750323a464e848856dc4c5c940663498beb74a)
- [OWASP - Cross-site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/)


================================================
FILE: cors/postmessage.html
================================================
<!DOCTYPE html>
<!--
/*
 * jQuery File Upload Plugin postMessage API
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */
-->
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>jQuery File Upload Plugin postMessage API</title>
    <script
      src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"
      integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
      crossorigin="anonymous"
    ></script>
  </head>
  <body>
    <script>
      'use strict';
      var origin = /^https:\/\/example.org/,
        target = new RegExp('^(http(s)?:)?\\/\\/' + location.host + '\\/');
      $(window).on('message', function (e) {
        e = e.originalEvent;
        var s = e.data,
          xhr = $.ajaxSettings.xhr(),
          f;
        if (!origin.test(e.origin)) {
          throw new Error('Origin "' + e.origin + '" does not match ' + origin);
        }
        if (!target.test(e.data.url)) {
          throw new Error(
            'Target "' + e.data.url + '" does not match ' + target
          );
        }
        $(xhr.upload).on('progress', function (ev) {
          ev = ev.originalEvent;
          e.source.postMessage(
            {
              id: s.id,
              type: ev.type,
              timeStamp: ev.timeStamp,
              lengthComputable: ev.lengthComputable,
              loaded: ev.loaded,
              total: ev.total
            },
            e.origin
          );
        });
        s.xhr = function () {
          return xhr;
        };
        if (!(s.data instanceof Blob)) {
          f = new FormData();
          $.each(s.data, function (i, v) {
            f.append(v.name, v.value);
          });
          s.data = f;
        }
        $.ajax(s).always(function (result, statusText, jqXHR) {
          if (!jqXHR.done) {
            jqXHR = result;
            result = null;
          }
          e.source.postMessage(
            {
              id: s.id,
              status: jqXHR.status,
              statusText: statusText,
              result: result,
              headers: jqXHR.getAllResponseHeaders()
            },
            e.origin
          );
        });
      });
    </script>
  </body>
</html>


================================================
FILE: cors/result.html
================================================
<!DOCTYPE html>
<!--
/*
 * jQuery Iframe Transport Plugin Redirect Page
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */
-->
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>jQuery Iframe Transport Plugin Redirect Page</title>
  </head>
  <body>
    <script>
      document.body.innerText = document.body.textContent = decodeURIComponent(
        window.location.search.slice(1)
      );
    </script>
  </body>
</html>


================================================
FILE: css/jquery.fileupload-noscript.css
================================================
@charset "UTF-8";
/*
 * jQuery File Upload Plugin NoScript CSS
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

.fileinput-button input {
  position: static;
  opacity: 1;
  filter: none;
  font-size: inherit !important;
  direction: inherit;
}
.fileinput-button span {
  display: none;
}


================================================
FILE: css/jquery.fileupload-ui-noscript.css
================================================
@charset "UTF-8";
/*
 * jQuery File Upload UI Plugin NoScript CSS
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2012, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

.fileinput-button i,
.fileupload-buttonbar .delete,
.fileupload-buttonbar .toggle {
  display: none;
}


================================================
FILE: css/jquery.fileupload-ui.css
================================================
@charset "UTF-8";
/*
 * jQuery File Upload UI Plugin CSS
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

.progress-animated .progress-bar,
.progress-animated .bar {
  background: url('../img/progressbar.gif') !important;
  filter: none;
}
.fileupload-process {
  float: right;
  display: none;
}
.fileupload-processing .fileupload-process,
.files .processing .preview {
  display: block;
  width: 32px;
  height: 32px;
  background: url('../img/loading.gif') center no-repeat;
  background-size: contain;
}
.files audio,
.files video {
  max-width: 300px;
}
.files .name {
  word-wrap: break-word;
  overflow-wrap: anywhere;
  -webkit-hyphens: auto;
  hyphens: auto;
}
.files button {
  margin-bottom: 5px;
}
.toggle[type='checkbox'] {
  transform: scale(2);
  margin-left: 10px;
}

@media (max-width: 767px) {
  .fileupload-buttonbar .btn {
    margin-bottom: 5px;
  }
  .fileupload-buttonbar .delete,
  .fileupload-buttonbar .toggle,
  .files .toggle,
  .files .btn span {
    display: none;
  }
  .files audio,
  .files video {
    max-width: 80px;
  }
}

@media (max-width: 480px) {
  .files .image td:nth-child(2) {
    display: none;
  }
}


================================================
FILE: css/jquery.fileupload.css
================================================
@charset "UTF-8";
/*
 * jQuery File Upload Plugin CSS
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

.fileinput-button {
  position: relative;
  overflow: hidden;
  display: inline-block;
}
.fileinput-button input {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  height: 100%;
  opacity: 0;
  filter: alpha(opacity=0);
  font-size: 200px !important;
  direction: ltr;
  cursor: pointer;
}

/* Fixes for IE < 8 */
@media screen\9 {
  .fileinput-button input {
    font-size: 150% !important;
  }
}


================================================
FILE: docker-compose.yml
================================================
version: '3.7'
services:
  example:
    build: server/php
    ports:
      - 127.0.0.1:80:80
    volumes:
      - .:/var/www/html
  mocha:
    image: blueimp/mocha-chrome
    command: http://example/test
    environment:
      - WAIT_FOR_HOSTS=example:80
    depends_on:
      - example
  chromedriver:
    image: blueimp/chromedriver
    tmpfs: /tmp
    environment:
      - DISABLE_X11=false
      - ENABLE_VNC=true
      - EXPOSE_X11=true
    volumes:
      - ./wdio/assets:/home/webdriver/assets:ro
    ports:
      - 127.0.0.1:5900:5900
  geckodriver:
    image: blueimp/geckodriver
    tmpfs: /tmp
    shm_size: 2g
    environment:
      - DISABLE_X11=false
      - ENABLE_VNC=true
      - EXPOSE_X11=true
    volumes:
      - ./wdio/assets:/home/webdriver/assets:ro
    ports:
      - 127.0.0.1:5901:5900
  wdio:
    image: blueimp/wdio
    read_only: true
    tmpfs:
      - /tmp
    environment:
      - WAIT_FOR_HOSTS=chromedriver:4444 geckodriver:4444 example:80
      - WINDOWS_HOST
      - MACOS_ASSETS_DIR=$PWD/wdio/assets/
      - WINDOWS_ASSETS_DIR
    volumes:
      - ./wdio:/app:ro
      - ./wdio/reports:/app/reports
    depends_on:
      - chromedriver
      - geckodriver
      - example


================================================
FILE: index.html
================================================
<!DOCTYPE html>
<!--
/*
 * jQuery File Upload Demo
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */
-->
<html lang="en">
  <head>
    <!-- Force latest IE rendering engine or ChromeFrame if installed -->
    <!--[if IE]>
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <![endif]-->
    <meta charset="utf-8" />
    <title>jQuery File Upload Demo</title>
    <meta
      name="description"
      content="File Upload widget with multiple file selection, drag&amp;drop support, progress bars, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. Works with any server-side platform (PHP, Python, Ruby on Rails, Java, Node.js, Go etc.) that supports standard HTML form file uploads."
    />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Bootstrap styles -->
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
      integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
      crossorigin="anonymous"
    />
    <!-- Generic page styles -->
    <style>
      #navigation {
        margin: 10px 0;
      }
      @media (max-width: 767px) {
        #title,
        #description {
          display: none;
        }
      }
    </style>
    <!-- blueimp Gallery styles -->
    <link
      rel="stylesheet"
      href="https://blueimp.github.io/Gallery/css/blueimp-gallery.min.css"
    />
    <!-- CSS to style the file input field as button and adjust the Bootstrap progress bars -->
    <link rel="stylesheet" href="css/jquery.fileupload.css" />
    <link rel="stylesheet" href="css/jquery.fileupload-ui.css" />
    <!-- CSS adjustments for browsers with JavaScript disabled -->
    <noscript
      ><link rel="stylesheet" href="css/jquery.fileupload-noscript.css"
    /></noscript>
    <noscript
      ><link rel="stylesheet" href="css/jquery.fileupload-ui-noscript.css"
    /></noscript>
  </head>
  <body>
    <div class="container">
      <ul class="nav nav-tabs" id="navigation">
        <li>
          <a href="https://github.com/blueimp/jQuery-File-Upload">Project</a>
        </li>
        <li class="active">
          <a href="#">Demo</a>
        </li>
        <li>
          <a href="https://github.com/blueimp/jQuery-File-Upload/wiki">Wiki</a>
        </li>
        <li>
          <a href="https://blueimp.net">Author</a>
        </li>
      </ul>
      <h1 id="title">jQuery File Upload Demo</h1>
      <blockquote id="description">
        <p>
          File Upload widget with multiple file selection, drag&amp;drop
          support, progress bars, validation and preview images, audio and video
          for jQuery.<br />
          Supports cross-domain, chunked and resumable file uploads and
          client-side image resizing.<br />
          Works with any server-side platform (PHP, Python, Ruby on Rails, Java,
          Node.js, Go etc.) that supports standard HTML form file uploads.
        </p>
      </blockquote>
      <!-- The file upload form used as target for the file upload widget -->
      <form
        id="fileupload"
        action="https://jquery-file-upload.appspot.com/"
        method="POST"
        enctype="multipart/form-data"
      >
        <!-- Redirect browsers with JavaScript disabled to the origin page -->
        <noscript
          ><input
            type="hidden"
            name="redirect"
            value="https://blueimp.github.io/jQuery-File-Upload/"
        /></noscript>
        <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
        <div class="row fileupload-buttonbar">
          <div class="col-lg-7">
            <!-- The fileinput-button span is used to style the file input field as button -->
            <span class="btn btn-success fileinput-button">
              <i class="glyphicon glyphicon-plus"></i>
              <span>Add files...</span>
              <input type="file" name="files[]" multiple />
            </span>
            <button type="submit" class="btn btn-primary start">
              <i class="glyphicon glyphicon-upload"></i>
              <span>Start upload</span>
            </button>
            <button type="reset" class="btn btn-warning cancel">
              <i class="glyphicon glyphicon-ban-circle"></i>
              <span>Cancel upload</span>
            </button>
            <button type="button" class="btn btn-danger delete">
              <i class="glyphicon glyphicon-trash"></i>
              <span>Delete selected</span>
            </button>
            <input type="checkbox" class="toggle" />
            <!-- The global file processing state -->
            <span class="fileupload-process"></span>
          </div>
          <!-- The global progress state -->
          <div class="col-lg-5 fileupload-progress fade">
            <!-- The global progress bar -->
            <div
              class="progress progress-striped active"
              role="progressbar"
              aria-valuemin="0"
              aria-valuemax="100"
            >
              <div
                class="progress-bar progress-bar-success"
                style="width: 0%;"
              ></div>
            </div>
            <!-- The extended global progress state -->
            <div class="progress-extended">&nbsp;</div>
          </div>
        </div>
        <!-- The table listing the files available for upload/download -->
        <table role="presentation" class="table table-striped">
          <tbody class="files"></tbody>
        </table>
      </form>
      <div class="panel panel-default">
        <div class="panel-heading">
          <h3 class="panel-title">Demo Notes</h3>
        </div>
        <div class="panel-body">
          <ul>
            <li>
              The maximum file size for uploads in this demo is
              <strong>999 KB</strong> (default file size is unlimited).
            </li>
            <li>
              Only image files (<strong>JPG, GIF, PNG</strong>) are allowed in
              this demo (by default there is no file type restriction).
            </li>
            <li>
              Uploaded files will be deleted automatically after
              <strong>5 minutes or less</strong> (demo files are stored in
              memory).
            </li>
            <li>
              You can <strong>drag &amp; drop</strong> files from your desktop
              on this webpage (see
              <a
                href="https://github.com/blueimp/jQuery-File-Upload/wiki/Browser-support"
                >Browser support</a
              >).
            </li>
            <li>
              Please refer to the
              <a href="https://github.com/blueimp/jQuery-File-Upload"
                >project website</a
              >
              and
              <a href="https://github.com/blueimp/jQuery-File-Upload/wiki"
                >documentation</a
              >
              for more information.
            </li>
            <li>
              Built with the
              <a href="https://getbootstrap.com/">Bootstrap</a> CSS framework
              and Icons from <a href="https://glyphicons.com/">Glyphicons</a>.
            </li>
          </ul>
        </div>
      </div>
    </div>
    <!-- The blueimp Gallery widget -->
    <div
      id="blueimp-gallery"
      class="blueimp-gallery blueimp-gallery-controls"
      aria-label="image gallery"
      aria-modal="true"
      role="dialog"
      data-filter=":even"
    >
      <div class="slides" aria-live="polite"></div>
      <h3 class="title"></h3>
      <a
        class="prev"
        aria-controls="blueimp-gallery"
        aria-label="previous slide"
        aria-keyshortcuts="ArrowLeft"
      ></a>
      <a
        class="next"
        aria-controls="blueimp-gallery"
        aria-label="next slide"
        aria-keyshortcuts="ArrowRight"
      ></a>
      <a
        class="close"
        aria-controls="blueimp-gallery"
        aria-label="close"
        aria-keyshortcuts="Escape"
      ></a>
      <a
        class="play-pause"
        aria-controls="blueimp-gallery"
        aria-label="play slideshow"
        aria-keyshortcuts="Space"
        aria-pressed="false"
        role="button"
      ></a>
      <ol class="indicator"></ol>
    </div>
    <!-- The template to display files available for upload -->
    <script id="template-upload" type="text/x-tmpl">
      {% for (var i=0, file; file=o.files[i]; i++) { %}
          <tr class="template-upload fade{%=o.options.loadImageFileTypes.test(file.type)?' image':''%}">
              <td>
                  <span class="preview"></span>
              </td>
              <td>
                  <p class="name">{%=file.name%}</p>
                  <strong class="error text-danger"></strong>
              </td>
              <td>
                  <p class="size">Processing...</p>
                  <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="progress-bar progress-bar-success" style="width:0%;"></div></div>
              </td>
              <td>
                  {% if (!o.options.autoUpload && o.options.edit && o.options.loadImageFileTypes.test(file.type)) { %}
                    <button class="btn btn-success edit" data-index="{%=i%}" disabled>
                        <i class="glyphicon glyphicon-edit"></i>
                        <span>Edit</span>
                    </button>
                  {% } %}
                  {% if (!i && !o.options.autoUpload) { %}
                      <button class="btn btn-primary start" disabled>
                          <i class="glyphicon glyphicon-upload"></i>
                          <span>Start</span>
                      </button>
                  {% } %}
                  {% if (!i) { %}
                      <button class="btn btn-warning cancel">
                          <i class="glyphicon glyphicon-ban-circle"></i>
                          <span>Cancel</span>
                      </button>
                  {% } %}
              </td>
          </tr>
      {% } %}
    </script>
    <!-- The template to display files available for download -->
    <script id="template-download" type="text/x-tmpl">
      {% for (var i=0, file; file=o.files[i]; i++) { %}
          <tr class="template-download fade{%=file.thumbnailUrl?' image':''%}">
              <td>
                  <span class="preview">
                      {% if (file.thumbnailUrl) { %}
                          <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" data-gallery><img src="{%=file.thumbnailUrl%}"></a>
                      {% } %}
                  </span>
              </td>
              <td>
                  <p class="name">
                      {% if (file.url) { %}
                          <a href="{%=file.url%}" title="{%=file.name%}" download="{%=file.name%}" {%=file.thumbnailUrl?'data-gallery':''%}>{%=file.name%}</a>
                      {% } else { %}
                          <span>{%=file.name%}</span>
                      {% } %}
                  </p>
                  {% if (file.error) { %}
                      <div><span class="label label-danger">Error</span> {%=file.error%}</div>
                  {% } %}
              </td>
              <td>
                  <span class="size">{%=o.formatFileSize(file.size)%}</span>
              </td>
              <td>
                  {% if (file.deleteUrl) { %}
                      <button class="btn btn-danger delete" data-type="{%=file.deleteType%}" data-url="{%=file.deleteUrl%}"{% if (file.deleteWithCredentials) { %} data-xhr-fields='{"withCredentials":true}'{% } %}>
                          <i class="glyphicon glyphicon-trash"></i>
                          <span>Delete</span>
                      </button>
                      <input type="checkbox" name="delete" value="1" class="toggle">
                  {% } else { %}
                      <button class="btn btn-warning cancel">
                          <i class="glyphicon glyphicon-ban-circle"></i>
                          <span>Cancel</span>
                      </button>
                  {% } %}
              </td>
          </tr>
      {% } %}
    </script>
    <script
      src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"
      integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
      crossorigin="anonymous"
    ></script>
    <!-- The jQuery UI widget factory, can be omitted if jQuery UI is already included -->
    <script src="js/vendor/jquery.ui.widget.js"></script>
    <!-- The Templates plugin is included to render the upload/download listings -->
    <script src="https://blueimp.github.io/JavaScript-Templates/js/tmpl.min.js"></script>
    <!-- The Load Image plugin is included for the preview images and image resizing functionality -->
    <script src="https://blueimp.github.io/JavaScript-Load-Image/js/load-image.all.min.js"></script>
    <!-- The Canvas to Blob plugin is included for image resizing functionality -->
    <script src="https://blueimp.github.io/JavaScript-Canvas-to-Blob/js/canvas-to-blob.min.js"></script>
    <!-- blueimp Gallery script -->
    <script src="https://blueimp.github.io/Gallery/js/jquery.blueimp-gallery.min.js"></script>
    <!-- The Iframe Transport is required for browsers without support for XHR file uploads -->
    <script src="js/jquery.iframe-transport.js"></script>
    <!-- The basic File Upload plugin -->
    <script src="js/jquery.fileupload.js"></script>
    <!-- The File Upload processing plugin -->
    <script src="js/jquery.fileupload-process.js"></script>
    <!-- The File Upload image preview & resize plugin -->
    <script src="js/jquery.fileupload-image.js"></script>
    <!-- The File Upload audio preview plugin -->
    <script src="js/jquery.fileupload-audio.js"></script>
    <!-- The File Upload video preview plugin -->
    <script src="js/jquery.fileupload-video.js"></script>
    <!-- The File Upload validation plugin -->
    <script src="js/jquery.fileupload-validate.js"></script>
    <!-- The File Upload user interface plugin -->
    <script src="js/jquery.fileupload-ui.js"></script>
    <!-- The main application script -->
    <script src="js/demo.js"></script>
    <!-- The XDomainRequest Transport is included for cross-domain file deletion for IE 8 and IE 9 -->
    <!--[if (gte IE 8)&(lt IE 10)]>
      <script src="js/cors/jquery.xdr-transport.js"></script>
    <![endif]-->
  </body>
</html>


================================================
FILE: js/cors/jquery.postmessage-transport.js
================================================
/*
 * jQuery postMessage Transport Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(require('jquery'));
  } else {
    // Browser globals:
    factory(window.jQuery);
  }
})(function ($) {
  'use strict';

  var counter = 0,
    names = [
      'accepts',
      'cache',
      'contents',
      'contentType',
      'crossDomain',
      'data',
      'dataType',
      'headers',
      'ifModified',
      'mimeType',
      'password',
      'processData',
      'timeout',
      'traditional',
      'type',
      'url',
      'username'
    ],
    convert = function (p) {
      return p;
    };

  $.ajaxSetup({
    converters: {
      'postmessage text': convert,
      'postmessage json': convert,
      'postmessage html': convert
    }
  });

  $.ajaxTransport('postmessage', function (options) {
    if (options.postMessage && window.postMessage) {
      var iframe,
        loc = $('<a></a>').prop('href', options.postMessage)[0],
        target = loc.protocol + '//' + loc.host,
        xhrUpload = options.xhr().upload;
      // IE always includes the port for the host property of a link
      // element, but not in the location.host or origin property for the
      // default http port 80 and https port 443, so we strip it:
      if (/^(http:\/\/.+:80)|(https:\/\/.+:443)$/.test(target)) {
        target = target.replace(/:(80|443)$/, '');
      }
      return {
        send: function (_, completeCallback) {
          counter += 1;
          var message = {
              id: 'postmessage-transport-' + counter
            },
            eventName = 'message.' + message.id;
          iframe = $(
            '<iframe style="display:none;" src="' +
              options.postMessage +
              '" name="' +
              message.id +
              '"></iframe>'
          )
            .on('load', function () {
              $.each(names, function (i, name) {
                message[name] = options[name];
              });
              message.dataType = message.dataType.replace('postmessage ', '');
              $(window).on(eventName, function (event) {
                var e = event.originalEvent;
                var data = e.data;
                var ev;
                if (e.origin === target && data.id === message.id) {
                  if (data.type === 'progress') {
                    ev = document.createEvent('Event');
                    ev.initEvent(data.type, false, true);
                    $.extend(ev, data);
                    xhrUpload.dispatchEvent(ev);
                  } else {
                    completeCallback(
                      data.status,
                      data.statusText,
                      { postmessage: data.result },
                      data.headers
                    );
                    iframe.remove();
                    $(window).off(eventName);
                  }
                }
              });
              iframe[0].contentWindow.postMessage(message, target);
            })
            .appendTo(document.body);
        },
        abort: function () {
          if (iframe) {
            iframe.remove();
          }
        }
      };
    }
  });
});


================================================
FILE: js/cors/jquery.xdr-transport.js
================================================
/*
 * jQuery XDomainRequest Transport Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 *
 * Based on Julian Aubourg's ajaxHooks xdr.js:
 * https://github.com/jaubourg/ajaxHooks/
 */

/* global define, require, XDomainRequest */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(require('jquery'));
  } else {
    // Browser globals:
    factory(window.jQuery);
  }
})(function ($) {
  'use strict';
  if (window.XDomainRequest && !$.support.cors) {
    $.ajaxTransport(function (s) {
      if (s.crossDomain && s.async) {
        if (s.timeout) {
          s.xdrTimeout = s.timeout;
          delete s.timeout;
        }
        var xdr;
        return {
          send: function (headers, completeCallback) {
            var addParamChar = /\?/.test(s.url) ? '&' : '?';
            /**
             * Callback wrapper function
             *
             * @param {number} status HTTP status code
             * @param {string} statusText HTTP status text
             * @param {object} [responses] Content-type specific responses
             * @param {string} [responseHeaders] Response headers string
             */
            function callback(status, statusText, responses, responseHeaders) {
              xdr.onload = xdr.onerror = xdr.ontimeout = $.noop;
              xdr = null;
              completeCallback(status, statusText, responses, responseHeaders);
            }
            xdr = new XDomainRequest();
            // XDomainRequest only supports GET and POST:
            if (s.type === 'DELETE') {
              s.url = s.url + addParamChar + '_method=DELETE';
              s.type = 'POST';
            } else if (s.type === 'PUT') {
              s.url = s.url + addParamChar + '_method=PUT';
              s.type = 'POST';
            } else if (s.type === 'PATCH') {
              s.url = s.url + addParamChar + '_method=PATCH';
              s.type = 'POST';
            }
            xdr.open(s.type, s.url);
            xdr.onload = function () {
              callback(
                200,
                'OK',
                { text: xdr.responseText },
                'Content-Type: ' + xdr.contentType
              );
            };
            xdr.onerror = function () {
              callback(404, 'Not Found');
            };
            if (s.xdrTimeout) {
              xdr.ontimeout = function () {
                callback(0, 'timeout');
              };
              xdr.timeout = s.xdrTimeout;
            }
            xdr.send((s.hasContent && s.data) || null);
          },
          abort: function () {
            if (xdr) {
              xdr.onerror = $.noop();
              xdr.abort();
            }
          }
        };
      }
    });
  }
});


================================================
FILE: js/demo.js
================================================
/*
 * jQuery File Upload Demo
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global $ */

$(function () {
  'use strict';

  // Initialize the jQuery File Upload widget:
  $('#fileupload').fileupload({
    // Uncomment the following to send cross-domain cookies:
    //xhrFields: {withCredentials: true},
    url: 'server/php/'
  });

  // Enable iframe cross-domain access via redirect option:
  $('#fileupload').fileupload(
    'option',
    'redirect',
    window.location.href.replace(/\/[^/]*$/, '/cors/result.html?%s')
  );

  if (window.location.hostname === 'blueimp.github.io') {
    // Demo settings:
    $('#fileupload').fileupload('option', {
      url: '//jquery-file-upload.appspot.com/',
      // Enable image resizing, except for Android and Opera,
      // which actually support image resizing, but fail to
      // send Blob objects via XHR requests:
      disableImageResize: /Android(?!.*Chrome)|Opera/.test(
        window.navigator.userAgent
      ),
      maxFileSize: 999000,
      acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i
    });
    // Upload server status check for browsers with CORS support:
    if ($.support.cors) {
      $.ajax({
        url: '//jquery-file-upload.appspot.com/',
        type: 'HEAD'
      }).fail(function () {
        $('<div class="alert alert-danger"></div>')
          .text('Upload server currently unavailable - ' + new Date())
          .appendTo('#fileupload');
      });
    }
  } else {
    // Load existing files:
    $('#fileupload').addClass('fileupload-processing');
    $.ajax({
      // Uncomment the following to send cross-domain cookies:
      //xhrFields: {withCredentials: true},
      url: $('#fileupload').fileupload('option', 'url'),
      dataType: 'json',
      context: $('#fileupload')[0]
    })
      .always(function () {
        $(this).removeClass('fileupload-processing');
      })
      .done(function (result) {
        $(this)
          .fileupload('option', 'done')
          // eslint-disable-next-line new-cap
          .call(this, $.Event('done'), { result: result });
      });
  }
});


================================================
FILE: js/jquery.fileupload-audio.js
================================================
/*
 * jQuery File Upload Audio Preview Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery', 'load-image', './jquery.fileupload-process'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(
      require('jquery'),
      require('blueimp-load-image/js/load-image'),
      require('./jquery.fileupload-process')
    );
  } else {
    // Browser globals:
    factory(window.jQuery, window.loadImage);
  }
})(function ($, loadImage) {
  'use strict';

  // Prepend to the default processQueue:
  $.blueimp.fileupload.prototype.options.processQueue.unshift(
    {
      action: 'loadAudio',
      // Use the action as prefix for the "@" options:
      prefix: true,
      fileTypes: '@',
      maxFileSize: '@',
      disabled: '@disableAudioPreview'
    },
    {
      action: 'setAudio',
      name: '@audioPreviewName',
      disabled: '@disableAudioPreview'
    }
  );

  // The File Upload Audio Preview plugin extends the fileupload widget
  // with audio preview functionality:
  $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    options: {
      // The regular expression for the types of audio files to load,
      // matched against the file type:
      loadAudioFileTypes: /^audio\/.*$/
    },

    _audioElement: document.createElement('audio'),

    processActions: {
      // Loads the audio file given via data.files and data.index
      // as audio element if the browser supports playing it.
      // Accepts the options fileTypes (regular expression)
      // and maxFileSize (integer) to limit the files to load:
      loadAudio: function (data, options) {
        if (options.disabled) {
          return data;
        }
        var file = data.files[data.index],
          url,
          audio;
        if (
          this._audioElement.canPlayType &&
          this._audioElement.canPlayType(file.type) &&
          ($.type(options.maxFileSize) !== 'number' ||
            file.size <= options.maxFileSize) &&
          (!options.fileTypes || options.fileTypes.test(file.type))
        ) {
          url = loadImage.createObjectURL(file);
          if (url) {
            audio = this._audioElement.cloneNode(false);
            audio.src = url;
            audio.controls = true;
            data.audio = audio;
            return data;
          }
        }
        return data;
      },

      // Sets the audio element as a property of the file object:
      setAudio: function (data, options) {
        if (data.audio && !options.disabled) {
          data.files[data.index][options.name || 'preview'] = data.audio;
        }
        return data;
      }
    }
  });
});


================================================
FILE: js/jquery.fileupload-image.js
================================================
/*
 * jQuery File Upload Image Preview & Resize Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define([
      'jquery',
      'load-image',
      'load-image-meta',
      'load-image-scale',
      'load-image-exif',
      'load-image-orientation',
      'canvas-to-blob',
      './jquery.fileupload-process'
    ], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(
      require('jquery'),
      require('blueimp-load-image/js/load-image'),
      require('blueimp-load-image/js/load-image-meta'),
      require('blueimp-load-image/js/load-image-scale'),
      require('blueimp-load-image/js/load-image-exif'),
      require('blueimp-load-image/js/load-image-orientation'),
      require('blueimp-canvas-to-blob'),
      require('./jquery.fileupload-process')
    );
  } else {
    // Browser globals:
    factory(window.jQuery, window.loadImage);
  }
})(function ($, loadImage) {
  'use strict';

  // Prepend to the default processQueue:
  $.blueimp.fileupload.prototype.options.processQueue.unshift(
    {
      action: 'loadImageMetaData',
      maxMetaDataSize: '@',
      disableImageHead: '@',
      disableMetaDataParsers: '@',
      disableExif: '@',
      disableExifOffsets: '@',
      includeExifTags: '@',
      excludeExifTags: '@',
      disableIptc: '@',
      disableIptcOffsets: '@',
      includeIptcTags: '@',
      excludeIptcTags: '@',
      disabled: '@disableImageMetaDataLoad'
    },
    {
      action: 'loadImage',
      // Use the action as prefix for the "@" options:
      prefix: true,
      fileTypes: '@',
      maxFileSize: '@',
      noRevoke: '@',
      disabled: '@disableImageLoad'
    },
    {
      action: 'resizeImage',
      // Use "image" as prefix for the "@" options:
      prefix: 'image',
      maxWidth: '@',
      maxHeight: '@',
      minWidth: '@',
      minHeight: '@',
      crop: '@',
      orientation: '@',
      forceResize: '@',
      disabled: '@disableImageResize',
      imageSmoothingQuality: '@imageSmoothingQuality'
    },
    {
      action: 'saveImage',
      quality: '@imageQuality',
      type: '@imageType',
      disabled: '@disableImageResize'
    },
    {
      action: 'saveImageMetaData',
      disabled: '@disableImageMetaDataSave'
    },
    {
      action: 'resizeImage',
      // Use "preview" as prefix for the "@" options:
      prefix: 'preview',
      maxWidth: '@',
      maxHeight: '@',
      minWidth: '@',
      minHeight: '@',
      crop: '@',
      orientation: '@',
      thumbnail: '@',
      canvas: '@',
      disabled: '@disableImagePreview'
    },
    {
      action: 'setImage',
      name: '@imagePreviewName',
      disabled: '@disableImagePreview'
    },
    {
      action: 'deleteImageReferences',
      disabled: '@disableImageReferencesDeletion'
    }
  );

  // The File Upload Resize plugin extends the fileupload widget
  // with image resize functionality:
  $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    options: {
      // The regular expression for the types of images to load:
      // matched against the file type:
      loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/,
      // The maximum file size of images to load:
      loadImageMaxFileSize: 10000000, // 10MB
      // The maximum width of resized images:
      imageMaxWidth: 1920,
      // The maximum height of resized images:
      imageMaxHeight: 1080,
      // Defines the image orientation (1-8) or takes the orientation
      // value from Exif data if set to true:
      imageOrientation: true,
      // Define if resized images should be cropped or only scaled:
      imageCrop: false,
      // Disable the resize image functionality by default:
      disableImageResize: true,
      // The maximum width of the preview images:
      previewMaxWidth: 80,
      // The maximum height of the preview images:
      previewMaxHeight: 80,
      // Defines the preview orientation (1-8) or takes the orientation
      // value from Exif data if set to true:
      previewOrientation: true,
      // Create the preview using the Exif data thumbnail:
      previewThumbnail: true,
      // Define if preview images should be cropped or only scaled:
      previewCrop: false,
      // Define if preview images should be resized as canvas elements:
      previewCanvas: true
    },

    processActions: {
      // Loads the image given via data.files and data.index
      // as img element, if the browser supports the File API.
      // Accepts the options fileTypes (regular expression)
      // and maxFileSize (integer) to limit the files to load:
      loadImage: function (data, options) {
        if (options.disabled) {
          return data;
        }
        var that = this,
          file = data.files[data.index],
          // eslint-disable-next-line new-cap
          dfd = $.Deferred();
        if (
          ($.type(options.maxFileSize) === 'number' &&
            file.size > options.maxFileSize) ||
          (options.fileTypes && !options.fileTypes.test(file.type)) ||
          !loadImage(
            file,
            function (img) {
              if (img.src) {
                data.img = img;
              }
              dfd.resolveWith(that, [data]);
            },
            options
          )
        ) {
          return data;
        }
        return dfd.promise();
      },

      // Resizes the image given as data.canvas or data.img
      // and updates data.canvas or data.img with the resized image.
      // Also stores the resized image as preview property.
      // Accepts the options maxWidth, maxHeight, minWidth,
      // minHeight, canvas and crop:
      resizeImage: function (data, options) {
        if (options.disabled || !(data.canvas || data.img)) {
          return data;
        }
        // eslint-disable-next-line no-param-reassign
        options = $.extend({ canvas: true }, options);
        var that = this,
          // eslint-disable-next-line new-cap
          dfd = $.Deferred(),
          img = (options.canvas && data.canvas) || data.img,
          resolve = function (newImg) {
            if (
              newImg &&
              (newImg.width !== img.width ||
                newImg.height !== img.height ||
                options.forceResize)
            ) {
              data[newImg.getContext ? 'canvas' : 'img'] = newImg;
            }
            data.preview = newImg;
            dfd.resolveWith(that, [data]);
          },
          thumbnail,
          thumbnailBlob;
        if (data.exif && options.thumbnail) {
          thumbnail = data.exif.get('Thumbnail');
          thumbnailBlob = thumbnail && thumbnail.get('Blob');
          if (thumbnailBlob) {
            options.orientation = data.exif.get('Orientation');
            loadImage(thumbnailBlob, resolve, options);
            return dfd.promise();
          }
        }
        if (data.orientation) {
          // Prevent orienting the same image twice:
          delete options.orientation;
        } else {
          data.orientation = options.orientation || loadImage.orientation;
        }
        if (img) {
          resolve(loadImage.scale(img, options, data));
          return dfd.promise();
        }
        return data;
      },

      // Saves the processed image given as data.canvas
      // inplace at data.index of data.files:
      saveImage: function (data, options) {
        if (!data.canvas || options.disabled) {
          return data;
        }
        var that = this,
          file = data.files[data.index],
          // eslint-disable-next-line new-cap
          dfd = $.Deferred();
        if (data.canvas.toBlob) {
          data.canvas.toBlob(
            function (blob) {
              if (!blob.name) {
                if (file.type === blob.type) {
                  blob.name = file.name;
                } else if (file.name) {
                  blob.name = file.name.replace(
                    /\.\w+$/,
                    '.' + blob.type.substr(6)
                  );
                }
              }
              // Don't restore invalid meta data:
              if (file.type !== blob.type) {
                delete data.imageHead;
              }
              // Store the created blob at the position
              // of the original file in the files list:
              data.files[data.index] = blob;
              dfd.resolveWith(that, [data]);
            },
            options.type || file.type,
            options.quality
          );
        } else {
          return data;
        }
        return dfd.promise();
      },

      loadImageMetaData: function (data, options) {
        if (options.disabled) {
          return data;
        }
        var that = this,
          // eslint-disable-next-line new-cap
          dfd = $.Deferred();
        loadImage.parseMetaData(
          data.files[data.index],
          function (result) {
            $.extend(data, result);
            dfd.resolveWith(that, [data]);
          },
          options
        );
        return dfd.promise();
      },

      saveImageMetaData: function (data, options) {
        if (
          !(
            data.imageHead &&
            data.canvas &&
            data.canvas.toBlob &&
            !options.disabled
          )
        ) {
          return data;
        }
        var that = this,
          file = data.files[data.index],
          // eslint-disable-next-line new-cap
          dfd = $.Deferred();
        if (data.orientation === true && data.exifOffsets) {
          // Reset Exif Orientation data:
          loadImage.writeExifData(data.imageHead, data, 'Orientation', 1);
        }
        loadImage.replaceHead(file, data.imageHead, function (blob) {
          blob.name = file.name;
          data.files[data.index] = blob;
          dfd.resolveWith(that, [data]);
        });
        return dfd.promise();
      },

      // Sets the resized version of the image as a property of the
      // file object, must be called after "saveImage":
      setImage: function (data, options) {
        if (data.preview && !options.disabled) {
          data.files[data.index][options.name || 'preview'] = data.preview;
        }
        return data;
      },

      deleteImageReferences: function (data, options) {
        if (!options.disabled) {
          delete data.img;
          delete data.canvas;
          delete data.preview;
          delete data.imageHead;
        }
        return data;
      }
    }
  });
});


================================================
FILE: js/jquery.fileupload-process.js
================================================
/*
 * jQuery File Upload Processing Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2012, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery', './jquery.fileupload'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(require('jquery'), require('./jquery.fileupload'));
  } else {
    // Browser globals:
    factory(window.jQuery);
  }
})(function ($) {
  'use strict';

  var originalAdd = $.blueimp.fileupload.prototype.options.add;

  // The File Upload Processing plugin extends the fileupload widget
  // with file processing functionality:
  $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    options: {
      // The list of processing actions:
      processQueue: [
        /*
                {
                    action: 'log',
                    type: 'debug'
                }
                */
      ],
      add: function (e, data) {
        var $this = $(this);
        data.process(function () {
          return $this.fileupload('process', data);
        });
        originalAdd.call(this, e, data);
      }
    },

    processActions: {
      /*
            log: function (data, options) {
                console[options.type](
                    'Processing "' + data.files[data.index].name + '"'
                );
            }
            */
    },

    _processFile: function (data, originalData) {
      var that = this,
        // eslint-disable-next-line new-cap
        dfd = $.Deferred().resolveWith(that, [data]),
        chain = dfd.promise();
      this._trigger('process', null, data);
      $.each(data.processQueue, function (i, settings) {
        var func = function (data) {
          if (originalData.errorThrown) {
            // eslint-disable-next-line new-cap
            return $.Deferred().rejectWith(that, [originalData]).promise();
          }
          return that.processActions[settings.action].call(
            that,
            data,
            settings
          );
        };
        chain = chain[that._promisePipe](func, settings.always && func);
      });
      chain
        .done(function () {
          that._trigger('processdone', null, data);
          that._trigger('processalways', null, data);
        })
        .fail(function () {
          that._trigger('processfail', null, data);
          that._trigger('processalways', null, data);
        });
      return chain;
    },

    // Replaces the settings of each processQueue item that
    // are strings starting with an "@", using the remaining
    // substring as key for the option map,
    // e.g. "@autoUpload" is replaced with options.autoUpload:
    _transformProcessQueue: function (options) {
      var processQueue = [];
      $.each(options.processQueue, function () {
        var settings = {},
          action = this.action,
          prefix = this.prefix === true ? action : this.prefix;
        $.each(this, function (key, value) {
          if ($.type(value) === 'string' && value.charAt(0) === '@') {
            settings[key] =
              options[
                value.slice(1) ||
                  (prefix
                    ? prefix + key.charAt(0).toUpperCase() + key.slice(1)
                    : key)
              ];
          } else {
            settings[key] = value;
          }
        });
        processQueue.push(settings);
      });
      options.processQueue = processQueue;
    },

    // Returns the number of files currently in the processing queue:
    processing: function () {
      return this._processing;
    },

    // Processes the files given as files property of the data parameter,
    // returns a Promise object that allows to bind callbacks:
    process: function (data) {
      var that = this,
        options = $.extend({}, this.options, data);
      if (options.processQueue && options.processQueue.length) {
        this._transformProcessQueue(options);
        if (this._processing === 0) {
          this._trigger('processstart');
        }
        $.each(data.files, function (index) {
          var opts = index ? $.extend({}, options) : options,
            func = function () {
              if (data.errorThrown) {
                // eslint-disable-next-line new-cap
                return $.Deferred().rejectWith(that, [data]).promise();
              }
              return that._processFile(opts, data);
            };
          opts.index = index;
          that._processing += 1;
          that._processingQueue = that._processingQueue[that._promisePipe](
            func,
            func
          ).always(function () {
            that._processing -= 1;
            if (that._processing === 0) {
              that._trigger('processstop');
            }
          });
        });
      }
      return this._processingQueue;
    },

    _create: function () {
      this._super();
      this._processing = 0;
      // eslint-disable-next-line new-cap
      this._processingQueue = $.Deferred().resolveWith(this).promise();
    }
  });
});


================================================
FILE: js/jquery.fileupload-ui.js
================================================
/*
 * jQuery File Upload User Interface Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define([
      'jquery',
      'blueimp-tmpl',
      './jquery.fileupload-image',
      './jquery.fileupload-audio',
      './jquery.fileupload-video',
      './jquery.fileupload-validate'
    ], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(
      require('jquery'),
      require('blueimp-tmpl'),
      require('./jquery.fileupload-image'),
      require('./jquery.fileupload-audio'),
      require('./jquery.fileupload-video'),
      require('./jquery.fileupload-validate')
    );
  } else {
    // Browser globals:
    factory(window.jQuery, window.tmpl);
  }
})(function ($, tmpl) {
  'use strict';

  $.blueimp.fileupload.prototype._specialOptions.push(
    'filesContainer',
    'uploadTemplateId',
    'downloadTemplateId'
  );

  // The UI version extends the file upload widget
  // and adds complete user interface interaction:
  $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    options: {
      // By default, files added to the widget are uploaded as soon
      // as the user clicks on the start buttons. To enable automatic
      // uploads, set the following option to true:
      autoUpload: false,
      // The class to show/hide UI elements:
      showElementClass: 'in',
      // The ID of the upload template:
      uploadTemplateId: 'template-upload',
      // The ID of the download template:
      downloadTemplateId: 'template-download',
      // The container for the list of files. If undefined, it is set to
      // an element with class "files" inside of the widget element:
      filesContainer: undefined,
      // By default, files are appended to the files container.
      // Set the following option to true, to prepend files instead:
      prependFiles: false,
      // The expected data type of the upload response, sets the dataType
      // option of the $.ajax upload requests:
      dataType: 'json',

      // Error and info messages:
      messages: {
        unknownError: 'Unknown error'
      },

      // Function returning the current number of files,
      // used by the maxNumberOfFiles validation:
      getNumberOfFiles: function () {
        return this.filesContainer.children().not('.processing').length;
      },

      // Callback to retrieve the list of files from the server response:
      getFilesFromResponse: function (data) {
        if (data.result && $.isArray(data.result.files)) {
          return data.result.files;
        }
        return [];
      },

      // The add callback is invoked as soon as files are added to the fileupload
      // widget (via file input selection, drag & drop or add API call).
      // See the basic file upload widget for more information:
      add: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var $this = $(this),
          that = $this.data('blueimp-fileupload') || $this.data('fileupload'),
          options = that.options;
        data.context = that
          ._renderUpload(data.files)
          .data('data', data)
          .addClass('processing');
        options.filesContainer[options.prependFiles ? 'prepend' : 'append'](
          data.context
        );
        that._forceReflow(data.context);
        that._transition(data.context);
        data
          .process(function () {
            return $this.fileupload('process', data);
          })
          .always(function () {
            data.context
              .each(function (index) {
                $(this)
                  .find('.size')
                  .text(that._formatFileSize(data.files[index].size));
              })
              .removeClass('processing');
            that._renderPreviews(data);
          })
          .done(function () {
            data.context.find('.edit,.start').prop('disabled', false);
            if (
              that._trigger('added', e, data) !== false &&
              (options.autoUpload || data.autoUpload) &&
              data.autoUpload !== false
            ) {
              data.submit();
            }
          })
          .fail(function () {
            if (data.files.error) {
              data.context.each(function (index) {
                var error = data.files[index].error;
                if (error) {
                  $(this).find('.error').text(error);
                }
              });
            }
          });
      },
      // Callback for the start of each file upload request:
      send: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var that =
          $(this).data('blueimp-fileupload') || $(this).data('fileupload');
        if (
          data.context &&
          data.dataType &&
          data.dataType.substr(0, 6) === 'iframe'
        ) {
          // Iframe Transport does not support progress events.
          // In lack of an indeterminate progress bar, we set
          // the progress to 100%, showing the full animated bar:
          data.context
            .find('.progress')
            .addClass(!$.support.transition && 'progress-animated')
            .attr('aria-valuenow', 100)
            .children()
            .first()
            .css('width', '100%');
        }
        return that._trigger('sent', e, data);
      },
      // Callback for successful uploads:
      done: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var that =
            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),
          getFilesFromResponse =
            data.getFilesFromResponse || that.options.getFilesFromResponse,
          files = getFilesFromResponse(data),
          template,
          deferred;
        if (data.context) {
          data.context.each(function (index) {
            var file = files[index] || { error: 'Empty file upload result' };
            deferred = that._addFinishedDeferreds();
            that._transition($(this)).done(function () {
              var node = $(this);
              template = that._renderDownload([file]).replaceAll(node);
              that._forceReflow(template);
              that._transition(template).done(function () {
                data.context = $(this);
                that._trigger('completed', e, data);
                that._trigger('finished', e, data);
                deferred.resolve();
              });
            });
          });
        } else {
          template = that
            ._renderDownload(files)
            [that.options.prependFiles ? 'prependTo' : 'appendTo'](
              that.options.filesContainer
            );
          that._forceReflow(template);
          deferred = that._addFinishedDeferreds();
          that._transition(template).done(function () {
            data.context = $(this);
            that._trigger('completed', e, data);
            that._trigger('finished', e, data);
            deferred.resolve();
          });
        }
      },
      // Callback for failed (abort or error) uploads:
      fail: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var that =
            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),
          template,
          deferred;
        if (data.context) {
          data.context.each(function (index) {
            if (data.errorThrown !== 'abort') {
              var file = data.files[index];
              file.error =
                file.error || data.errorThrown || data.i18n('unknownError');
              deferred = that._addFinishedDeferreds();
              that._transition($(this)).done(function () {
                var node = $(this);
                template = that._renderDownload([file]).replaceAll(node);
                that._forceReflow(template);
                that._transition(template).done(function () {
                  data.context = $(this);
                  that._trigger('failed', e, data);
                  that._trigger('finished', e, data);
                  deferred.resolve();
                });
              });
            } else {
              deferred = that._addFinishedDeferreds();
              that._transition($(this)).done(function () {
                $(this).remove();
                that._trigger('failed', e, data);
                that._trigger('finished', e, data);
                deferred.resolve();
              });
            }
          });
        } else if (data.errorThrown !== 'abort') {
          data.context = that
            ._renderUpload(data.files)
            [that.options.prependFiles ? 'prependTo' : 'appendTo'](
              that.options.filesContainer
            )
            .data('data', data);
          that._forceReflow(data.context);
          deferred = that._addFinishedDeferreds();
          that._transition(data.context).done(function () {
            data.context = $(this);
            that._trigger('failed', e, data);
            that._trigger('finished', e, data);
            deferred.resolve();
          });
        } else {
          that._trigger('failed', e, data);
          that._trigger('finished', e, data);
          that._addFinishedDeferreds().resolve();
        }
      },
      // Callback for upload progress events:
      progress: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var progress = Math.floor((data.loaded / data.total) * 100);
        if (data.context) {
          data.context.each(function () {
            $(this)
              .find('.progress')
              .attr('aria-valuenow', progress)
              .children()
              .first()
              .css('width', progress + '%');
          });
        }
      },
      // Callback for global upload progress events:
      progressall: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var $this = $(this),
          progress = Math.floor((data.loaded / data.total) * 100),
          globalProgressNode = $this.find('.fileupload-progress'),
          extendedProgressNode = globalProgressNode.find('.progress-extended');
        if (extendedProgressNode.length) {
          extendedProgressNode.html(
            (
              $this.data('blueimp-fileupload') || $this.data('fileupload')
            )._renderExtendedProgress(data)
          );
        }
        globalProgressNode
          .find('.progress')
          .attr('aria-valuenow', progress)
          .children()
          .first()
          .css('width', progress + '%');
      },
      // Callback for uploads start, equivalent to the global ajaxStart event:
      start: function (e) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var that =
          $(this).data('blueimp-fileupload') || $(this).data('fileupload');
        that._resetFinishedDeferreds();
        that
          ._transition($(this).find('.fileupload-progress'))
          .done(function () {
            that._trigger('started', e);
          });
      },
      // Callback for uploads stop, equivalent to the global ajaxStop event:
      stop: function (e) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var that =
            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),
          deferred = that._addFinishedDeferreds();
        $.when.apply($, that._getFinishedDeferreds()).done(function () {
          that._trigger('stopped', e);
        });
        that
          ._transition($(this).find('.fileupload-progress'))
          .done(function () {
            $(this)
              .find('.progress')
              .attr('aria-valuenow', '0')
              .children()
              .first()
              .css('width', '0%');
            $(this).find('.progress-extended').html('&nbsp;');
            deferred.resolve();
          });
      },
      processstart: function (e) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        $(this).addClass('fileupload-processing');
      },
      processstop: function (e) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        $(this).removeClass('fileupload-processing');
      },
      // Callback for file deletion:
      destroy: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        var that =
            $(this).data('blueimp-fileupload') || $(this).data('fileupload'),
          removeNode = function () {
            that._transition(data.context).done(function () {
              $(this).remove();
              that._trigger('destroyed', e, data);
            });
          };
        if (data.url) {
          data.dataType = data.dataType || that.options.dataType;
          $.ajax(data)
            .done(removeNode)
            .fail(function () {
              that._trigger('destroyfailed', e, data);
            });
        } else {
          removeNode();
        }
      }
    },

    _resetFinishedDeferreds: function () {
      this._finishedUploads = [];
    },

    _addFinishedDeferreds: function (deferred) {
      // eslint-disable-next-line new-cap
      var promise = deferred || $.Deferred();
      this._finishedUploads.push(promise);
      return promise;
    },

    _getFinishedDeferreds: function () {
      return this._finishedUploads;
    },

    // Link handler, that allows to download files
    // by drag & drop of the links to the desktop:
    _enableDragToDesktop: function () {
      var link = $(this),
        url = link.prop('href'),
        name = link.prop('download'),
        type = 'application/octet-stream';
      link.on('dragstart', function (e) {
        try {
          e.originalEvent.dataTransfer.setData(
            'DownloadURL',
            [type, name, url].join(':')
          );
        } catch (ignore) {
          // Ignore exceptions
        }
      });
    },

    _formatFileSize: function (bytes) {
      if (typeof bytes !== 'number') {
        return '';
      }
      if (bytes >= 1000000000) {
        return (bytes / 1000000000).toFixed(2) + ' GB';
      }
      if (bytes >= 1000000) {
        return (bytes / 1000000).toFixed(2) + ' MB';
      }
      return (bytes / 1000).toFixed(2) + ' KB';
    },

    _formatBitrate: function (bits) {
      if (typeof bits !== 'number') {
        return '';
      }
      if (bits >= 1000000000) {
        return (bits / 1000000000).toFixed(2) + ' Gbit/s';
      }
      if (bits >= 1000000) {
        return (bits / 1000000).toFixed(2) + ' Mbit/s';
      }
      if (bits >= 1000) {
        return (bits / 1000).toFixed(2) + ' kbit/s';
      }
      return bits.toFixed(2) + ' bit/s';
    },

    _formatTime: function (seconds) {
      var date = new Date(seconds * 1000),
        days = Math.floor(seconds / 86400);
      days = days ? days + 'd ' : '';
      return (
        days +
        ('0' + date.getUTCHours()).slice(-2) +
        ':' +
        ('0' + date.getUTCMinutes()).slice(-2) +
        ':' +
        ('0' + date.getUTCSeconds()).slice(-2)
      );
    },

    _formatPercentage: function (floatValue) {
      return (floatValue * 100).toFixed(2) + ' %';
    },

    _renderExtendedProgress: function (data) {
      return (
        this._formatBitrate(data.bitrate) +
        ' | ' +
        this._formatTime(((data.total - data.loaded) * 8) / data.bitrate) +
        ' | ' +
        this._formatPercentage(data.loaded / data.total) +
        ' | ' +
        this._formatFileSize(data.loaded) +
        ' / ' +
        this._formatFileSize(data.total)
      );
    },

    _renderTemplate: function (func, files) {
      if (!func) {
        return $();
      }
      var result = func({
        files: files,
        formatFileSize: this._formatFileSize,
        options: this.options
      });
      if (result instanceof $) {
        return result;
      }
      return $(this.options.templatesContainer).html(result).children();
    },

    _renderPreviews: function (data) {
      data.context.find('.preview').each(function (index, elm) {
        $(elm).empty().append(data.files[index].preview);
      });
    },

    _renderUpload: function (files) {
      return this._renderTemplate(this.options.uploadTemplate, files);
    },

    _renderDownload: function (files) {
      return this._renderTemplate(this.options.downloadTemplate, files)
        .find('a[download]')
        .each(this._enableDragToDesktop)
        .end();
    },

    _editHandler: function (e) {
      e.preventDefault();
      if (!this.options.edit) return;
      var that = this,
        button = $(e.currentTarget),
        template = button.closest('.template-upload'),
        data = template.data('data'),
        index = button.data().index;
      this.options.edit(data.files[index]).then(function (file) {
        if (!file) return;
        data.files[index] = file;
        data.context.addClass('processing');
        template.find('.edit,.start').prop('disabled', true);
        $(that.element)
          .fileupload('process', data)
          .always(function () {
            template
              .find('.size')
              .text(that._formatFileSize(data.files[index].size));
            data.context.removeClass('processing');
            that._renderPreviews(data);
          })
          .done(function () {
            template.find('.edit,.start').prop('disabled', false);
          })
          .fail(function () {
            template.find('.edit').prop('disabled', false);
            var error = data.files[index].error;
            if (error) {
              template.find('.error').text(error);
            }
          });
      });
    },

    _startHandler: function (e) {
      e.preventDefault();
      var button = $(e.currentTarget),
        template = button.closest('.template-upload'),
        data = template.data('data');
      button.prop('disabled', true);
      if (data && data.submit) {
        data.submit();
      }
    },

    _cancelHandler: function (e) {
      e.preventDefault();
      var template = $(e.currentTarget).closest(
          '.template-upload,.template-download'
        ),
        data = template.data('data') || {};
      data.context = data.context || template;
      if (data.abort) {
        data.abort();
      } else {
        data.errorThrown = 'abort';
        this._trigger('fail', e, data);
      }
    },

    _deleteHandler: function (e) {
      e.preventDefault();
      var button = $(e.currentTarget);
      this._trigger(
        'destroy',
        e,
        $.extend(
          {
            context: button.closest('.template-download'),
            type: 'DELETE'
          },
          button.data()
        )
      );
    },

    _forceReflow: function (node) {
      return $.support.transition && node.length && node[0].offsetWidth;
    },

    _transition: function (node) {
      // eslint-disable-next-line new-cap
      var dfd = $.Deferred();
      if (
        $.support.transition &&
        node.hasClass('fade') &&
        node.is(':visible')
      ) {
        var transitionEndHandler = function (e) {
          // Make sure we don't respond to other transition events
          // in the container element, e.g. from button elements:
          if (e.target === node[0]) {
            node.off($.support.transition.end, transitionEndHandler);
            dfd.resolveWith(node);
          }
        };
        node
          .on($.support.transition.end, transitionEndHandler)
          .toggleClass(this.options.showElementClass);
      } else {
        node.toggleClass(this.options.showElementClass);
        dfd.resolveWith(node);
      }
      return dfd;
    },

    _initButtonBarEventHandlers: function () {
      var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
        filesList = this.options.filesContainer;
      this._on(fileUploadButtonBar.find('.start'), {
        click: function (e) {
          e.preventDefault();
          filesList.find('.start').trigger('click');
        }
      });
      this._on(fileUploadButtonBar.find('.cancel'), {
        click: function (e) {
          e.preventDefault();
          filesList.find('.cancel').trigger('click');
        }
      });
      this._on(fileUploadButtonBar.find('.delete'), {
        click: function (e) {
          e.preventDefault();
          filesList
            .find('.toggle:checked')
            .closest('.template-download')
            .find('.delete')
            .trigger('click');
          fileUploadButtonBar.find('.toggle').prop('checked', false);
        }
      });
      this._on(fileUploadButtonBar.find('.toggle'), {
        change: function (e) {
          filesList
            .find('.toggle')
            .prop('checked', $(e.currentTarget).is(':checked'));
        }
      });
    },

    _destroyButtonBarEventHandlers: function () {
      this._off(
        this.element
          .find('.fileupload-buttonbar')
          .find('.start, .cancel, .delete'),
        'click'
      );
      this._off(this.element.find('.fileupload-buttonbar .toggle'), 'change.');
    },

    _initEventHandlers: function () {
      this._super();
      this._on(this.options.filesContainer, {
        'click .edit': this._editHandler,
        'click .start': this._startHandler,
        'click .cancel': this._cancelHandler,
        'click .delete': this._deleteHandler
      });
      this._initButtonBarEventHandlers();
    },

    _destroyEventHandlers: function () {
      this._destroyButtonBarEventHandlers();
      this._off(this.options.filesContainer, 'click');
      this._super();
    },

    _enableFileInputButton: function () {
      this.element
        .find('.fileinput-button input')
        .prop('disabled', false)
        .parent()
        .removeClass('disabled');
    },

    _disableFileInputButton: function () {
      this.element
        .find('.fileinput-button input')
        .prop('disabled', true)
        .parent()
        .addClass('disabled');
    },

    _initTemplates: function () {
      var options = this.options;
      options.templatesContainer = this.document[0].createElement(
        options.filesContainer.prop('nodeName')
      );
      if (tmpl) {
        if (options.uploadTemplateId) {
          options.uploadTemplate = tmpl(options.uploadTemplateId);
        }
        if (options.downloadTemplateId) {
          options.downloadTemplate = tmpl(options.downloadTemplateId);
        }
      }
    },

    _initFilesContainer: function () {
      var options = this.options;
      if (options.filesContainer === undefined) {
        options.filesContainer = this.element.find('.files');
      } else if (!(options.filesContainer instanceof $)) {
        options.filesContainer = $(options.filesContainer);
      }
    },

    _initSpecialOptions: function () {
      this._super();
      this._initFilesContainer();
      this._initTemplates();
    },

    _create: function () {
      this._super();
      this._resetFinishedDeferreds();
      if (!$.support.fileInput) {
        this._disableFileInputButton();
      }
    },

    enable: function () {
      var wasDisabled = false;
      if (this.options.disabled) {
        wasDisabled = true;
      }
      this._super();
      if (wasDisabled) {
        this.element.find('input, button').prop('disabled', false);
        this._enableFileInputButton();
      }
    },

    disable: function () {
      if (!this.options.disabled) {
        this.element.find('input, button').prop('disabled', true);
        this._disableFileInputButton();
      }
      this._super();
    }
  });
});


================================================
FILE: js/jquery.fileupload-validate.js
================================================
/*
 * jQuery File Upload Validation Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery', './jquery.fileupload-process'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(require('jquery'), require('./jquery.fileupload-process'));
  } else {
    // Browser globals:
    factory(window.jQuery);
  }
})(function ($) {
  'use strict';

  // Append to the default processQueue:
  $.blueimp.fileupload.prototype.options.processQueue.push({
    action: 'validate',
    // Always trigger this action,
    // even if the previous action was rejected:
    always: true,
    // Options taken from the global options map:
    acceptFileTypes: '@',
    maxFileSize: '@',
    minFileSize: '@',
    maxNumberOfFiles: '@',
    disabled: '@disableValidation'
  });

  // The File Upload Validation plugin extends the fileupload widget
  // with file validation functionality:
  $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    options: {
      /*
            // The regular expression for allowed file types, matches
            // against either file type or file name:
            acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
            // The maximum allowed file size in bytes:
            maxFileSize: 10000000, // 10 MB
            // The minimum allowed file size in bytes:
            minFileSize: undefined, // No minimal file size
            // The limit of files to be uploaded:
            maxNumberOfFiles: 10,
            */

      // Function returning the current number of files,
      // has to be overridden for maxNumberOfFiles validation:
      getNumberOfFiles: $.noop,

      // Error and info messages:
      messages: {
        maxNumberOfFiles: 'Maximum number of files exceeded',
        acceptFileTypes: 'File type not allowed',
        maxFileSize: 'File is too large',
        minFileSize: 'File is too small'
      }
    },

    processActions: {
      validate: function (data, options) {
        if (options.disabled) {
          return data;
        }
        // eslint-disable-next-line new-cap
        var dfd = $.Deferred(),
          settings = this.options,
          file = data.files[data.index],
          fileSize;
        if (options.minFileSize || options.maxFileSize) {
          fileSize = file.size;
        }
        if (
          $.type(options.maxNumberOfFiles) === 'number' &&
          (settings.getNumberOfFiles() || 0) + data.files.length >
            options.maxNumberOfFiles
        ) {
          file.error = settings.i18n('maxNumberOfFiles');
        } else if (
          options.acceptFileTypes &&
          !(
            options.acceptFileTypes.test(file.type) ||
            options.acceptFileTypes.test(file.name)
          )
        ) {
          file.error = settings.i18n('acceptFileTypes');
        } else if (fileSize > options.maxFileSize) {
          file.error = settings.i18n('maxFileSize');
        } else if (
          $.type(fileSize) === 'number' &&
          fileSize < options.minFileSize
        ) {
          file.error = settings.i18n('minFileSize');
        } else {
          delete file.error;
        }
        if (file.error || data.files.error) {
          data.files.error = true;
          dfd.rejectWith(this, [data]);
        } else {
          dfd.resolveWith(this, [data]);
        }
        return dfd.promise();
      }
    }
  });
});


================================================
FILE: js/jquery.fileupload-video.js
================================================
/*
 * jQuery File Upload Video Preview Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2013, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery', 'load-image', './jquery.fileupload-process'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(
      require('jquery'),
      require('blueimp-load-image/js/load-image'),
      require('./jquery.fileupload-process')
    );
  } else {
    // Browser globals:
    factory(window.jQuery, window.loadImage);
  }
})(function ($, loadImage) {
  'use strict';

  // Prepend to the default processQueue:
  $.blueimp.fileupload.prototype.options.processQueue.unshift(
    {
      action: 'loadVideo',
      // Use the action as prefix for the "@" options:
      prefix: true,
      fileTypes: '@',
      maxFileSize: '@',
      disabled: '@disableVideoPreview'
    },
    {
      action: 'setVideo',
      name: '@videoPreviewName',
      disabled: '@disableVideoPreview'
    }
  );

  // The File Upload Video Preview plugin extends the fileupload widget
  // with video preview functionality:
  $.widget('blueimp.fileupload', $.blueimp.fileupload, {
    options: {
      // The regular expression for the types of video files to load,
      // matched against the file type:
      loadVideoFileTypes: /^video\/.*$/
    },

    _videoElement: document.createElement('video'),

    processActions: {
      // Loads the video file given via data.files and data.index
      // as video element if the browser supports playing it.
      // Accepts the options fileTypes (regular expression)
      // and maxFileSize (integer) to limit the files to load:
      loadVideo: function (data, options) {
        if (options.disabled) {
          return data;
        }
        var file = data.files[data.index],
          url,
          video;
        if (
          this._videoElement.canPlayType &&
          this._videoElement.canPlayType(file.type) &&
          ($.type(options.maxFileSize) !== 'number' ||
            file.size <= options.maxFileSize) &&
          (!options.fileTypes || options.fileTypes.test(file.type))
        ) {
          url = loadImage.createObjectURL(file);
          if (url) {
            video = this._videoElement.cloneNode(false);
            video.src = url;
            video.controls = true;
            data.video = video;
            return data;
          }
        }
        return data;
      },

      // Sets the video element as a property of the file object:
      setVideo: function (data, options) {
        if (data.video && !options.disabled) {
          data.files[data.index][options.name || 'preview'] = data.video;
        }
        return data;
      }
    }
  });
});


================================================
FILE: js/jquery.fileupload.js
================================================
/*
 * jQuery File Upload Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */
/* eslint-disable new-cap */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery', 'jquery-ui/ui/widget'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(require('jquery'), require('./vendor/jquery.ui.widget'));
  } else {
    // Browser globals:
    factory(window.jQuery);
  }
})(function ($) {
  'use strict';

  // Detect file input support, based on
  // https://viljamis.com/2012/file-upload-support-on-mobile/
  $.support.fileInput = !(
    new RegExp(
      // Handle devices which give false positives for the feature detection:
      '(Android (1\\.[0156]|2\\.[01]))' +
        '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
        '|(w(eb)?OSBrowser)|(webOS)' +
        '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
    ).test(window.navigator.userAgent) ||
    // Feature detection for all other devices:
    $('<input type="file"/>').prop('disabled')
  );

  // The FileReader API is not actually used, but works as feature detection,
  // as some Safari versions (5?) support XHR file uploads via the FormData API,
  // but not non-multipart XHR file uploads.
  // window.XMLHttpRequestUpload is not available on IE10, so we check for
  // window.ProgressEvent instead to detect XHR2 file upload capability:
  $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
  $.support.xhrFormDataFileUpload = !!window.FormData;

  // Detect support for Blob slicing (required for chunked uploads):
  $.support.blobSlice =
    window.Blob &&
    (Blob.prototype.slice ||
      Blob.prototype.webkitSlice ||
      Blob.prototype.mozSlice);

  /**
   * Helper function to create drag handlers for dragover/dragenter/dragleave
   *
   * @param {string} type Event type
   * @returns {Function} Drag handler
   */
  function getDragHandler(type) {
    var isDragOver = type === 'dragover';
    return function (e) {
      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
      var dataTransfer = e.dataTransfer;
      if (
        dataTransfer &&
        $.inArray('Files', dataTransfer.types) !== -1 &&
        this._trigger(type, $.Event(type, { delegatedEvent: e })) !== false
      ) {
        e.preventDefault();
        if (isDragOver) {
          dataTransfer.dropEffect = 'copy';
        }
      }
    };
  }

  // The fileupload widget listens for change events on file input fields defined
  // via fileInput setting and paste or drop events of the given dropZone.
  // In addition to the default jQuery Widget methods, the fileupload widget
  // exposes the "add" and "send" methods, to add or directly send files using
  // the fileupload API.
  // By default, files added via file input selection, paste, drag & drop or
  // "add" method are uploaded immediately, but it is possible to override
  // the "add" callback option to queue file uploads.
  $.widget('blueimp.fileupload', {
    options: {
      // The drop target element(s), by the default the complete document.
      // Set to null to disable drag & drop support:
      dropZone: $(document),
      // The paste target element(s), by the default undefined.
      // Set to a DOM node or jQuery object to enable file pasting:
      pasteZone: undefined,
      // The file input field(s), that are listened to for change events.
      // If undefined, it is set to the file input fields inside
      // of the widget element on plugin initialization.
      // Set to null to disable the change listener.
      fileInput: undefined,
      // By default, the file input field is replaced with a clone after
      // each input field change event. This is required for iframe transport
      // queues and allows change events to be fired for the same file
      // selection, but can be disabled by setting the following option to false:
      replaceFileInput: true,
      // The parameter name for the file form data (the request argument name).
      // If undefined or empty, the name property of the file input field is
      // used, or "files[]" if the file input name property is also empty,
      // can be a string or an array of strings:
      paramName: undefined,
      // By default, each file of a selection is uploaded using an individual
      // request for XHR type uploads. Set to false to upload file
      // selections in one request each:
      singleFileUploads: true,
      // To limit the number of files uploaded with one XHR request,
      // set the following option to an integer greater than 0:
      limitMultiFileUploads: undefined,
      // The following option limits the number of files uploaded with one
      // XHR request to keep the request size under or equal to the defined
      // limit in bytes:
      limitMultiFileUploadSize: undefined,
      // Multipart file uploads add a number of bytes to each uploaded file,
      // therefore the following option adds an overhead for each file used
      // in the limitMultiFileUploadSize configuration:
      limitMultiFileUploadSizeOverhead: 512,
      // Set the following option to true to issue all file upload requests
      // in a sequential order:
      sequentialUploads: false,
      // To limit the number of concurrent uploads,
      // set the following option to an integer greater than 0:
      limitConcurrentUploads: undefined,
      // Set the following option to true to force iframe transport uploads:
      forceIframeTransport: false,
      // Set the following option to the location of a redirect url on the
      // origin server, for cross-domain iframe transport uploads:
      redirect: undefined,
      // The parameter name for the redirect url, sent as part of the form
      // data and set to 'redirect' if this option is empty:
      redirectParamName: undefined,
      // Set the following option to the location of a postMessage window,
      // to enable postMessage transport uploads:
      postMessage: undefined,
      // By default, XHR file uploads are sent as multipart/form-data.
      // The iframe transport is always using multipart/form-data.
      // Set to false to enable non-multipart XHR uploads:
      multipart: true,
      // To upload large files in smaller chunks, set the following option
      // to a preferred maximum chunk size. If set to 0, null or undefined,
      // or the browser does not support the required Blob API, files will
      // be uploaded as a whole.
      maxChunkSize: undefined,
      // When a non-multipart upload or a chunked multipart upload has been
      // aborted, this option can be used to resume the upload by setting
      // it to the size of the already uploaded bytes. This option is most
      // useful when modifying the options object inside of the "add" or
      // "send" callbacks, as the options are cloned for each file upload.
      uploadedBytes: undefined,
      // By default, failed (abort or error) file uploads are removed from the
      // global progress calculation. Set the following option to false to
      // prevent recalculating the global progress data:
      recalculateProgress: true,
      // Interval in milliseconds to calculate and trigger progress events:
      progressInterval: 100,
      // Interval in milliseconds to calculate progress bitrate:
      bitrateInterval: 500,
      // By default, uploads are started automatically when adding files:
      autoUpload: true,
      // By default, duplicate file names are expected to be handled on
      // the server-side. If this is not possible (e.g. when uploading
      // files directly to Amazon S3), the following option can be set to
      // an empty object or an object mapping existing filenames, e.g.:
      // { "image.jpg": true, "image (1).jpg": true }
      // If it is set, all files will be uploaded with unique filenames,
      // adding increasing number suffixes if necessary, e.g.:
      // "image (2).jpg"
      uniqueFilenames: undefined,

      // Error and info messages:
      messages: {
        uploadedBytes: 'Uploaded bytes exceed file size'
      },

      // Translation function, gets the message key to be translated
      // and an object with context specific data as arguments:
      i18n: function (message, context) {
        // eslint-disable-next-line no-param-reassign
        message = this.messages[message] || message.toString();
        if (context) {
          $.each(context, function (key, value) {
            // eslint-disable-next-line no-param-reassign
            message = message.replace('{' + key + '}', value);
          });
        }
        return message;
      },

      // Additional form data to be sent along with the file uploads can be set
      // using this option, which accepts an array of objects with name and
      // value properties, a function returning such an array, a FormData
      // object (for XHR file uploads), or a simple object.
      // The form of the first fileInput is given as parameter to the function:
      formData: function (form) {
        return form.serializeArray();
      },

      // The add callback is invoked as soon as files are added to the fileupload
      // widget (via file input selection, drag & drop, paste or add API call).
      // If the singleFileUploads option is enabled, this callback will be
      // called once for each file in the selection for XHR file uploads, else
      // once for each file selection.
      //
      // The upload starts when the submit method is invoked on the data parameter.
      // The data object contains a files property holding the added files
      // and allows you to override plugin options as well as define ajax settings.
      //
      // Listeners for this callback can also be bound the following way:
      // .on('fileuploadadd', func);
      //
      // data.submit() returns a Promise object and allows to attach additional
      // handlers using jQuery's Deferred callbacks:
      // data.submit().done(func).fail(func).always(func);
      add: function (e, data) {
        if (e.isDefaultPrevented()) {
          return false;
        }
        if (
          data.autoUpload ||
          (data.autoUpload !== false &&
            $(this).fileupload('option', 'autoUpload'))
        ) {
          data.process().done(function () {
            data.submit();
          });
        }
      },

      // Other callbacks:

      // Callback for the submit event of each file upload:
      // submit: function (e, data) {}, // .on('fileuploadsubmit', func);

      // Callback for the start of each file upload request:
      // send: function (e, data) {}, // .on('fileuploadsend', func);

      // Callback for successful uploads:
      // done: function (e, data) {}, // .on('fileuploaddone', func);

      // Callback for failed (abort or error) uploads:
      // fail: function (e, data) {}, // .on('fileuploadfail', func);

      // Callback for completed (success, abort or error) requests:
      // always: function (e, data) {}, // .on('fileuploadalways', func);

      // Callback for upload progress events:
      // progress: function (e, data) {}, // .on('fileuploadprogress', func);

      // Callback for global upload progress events:
      // progressall: function (e, data) {}, // .on('fileuploadprogressall', func);

      // Callback for uploads start, equivalent to the global ajaxStart event:
      // start: function (e) {}, // .on('fileuploadstart', func);

      // Callback for uploads stop, equivalent to the global ajaxStop event:
      // stop: function (e) {}, // .on('fileuploadstop', func);

      // Callback for change events of the fileInput(s):
      // change: function (e, data) {}, // .on('fileuploadchange', func);

      // Callback for paste events to the pasteZone(s):
      // paste: function (e, data) {}, // .on('fileuploadpaste', func);

      // Callback for drop events of the dropZone(s):
      // drop: function (e, data) {}, // .on('fileuploaddrop', func);

      // Callback for dragover events of the dropZone(s):
      // dragover: function (e) {}, // .on('fileuploaddragover', func);

      // Callback before the start of each chunk upload request (before form data initialization):
      // chunkbeforesend: function (e, data) {}, // .on('fileuploadchunkbeforesend', func);

      // Callback for the start of each chunk upload request:
      // chunksend: function (e, data) {}, // .on('fileuploadchunksend', func);

      // Callback for successful chunk uploads:
      // chunkdone: function (e, data) {}, // .on('fileuploadchunkdone', func);

      // Callback for failed (abort or error) chunk uploads:
      // chunkfail: function (e, data) {}, // .on('fileuploadchunkfail', func);

      // Callback for completed (success, abort or error) chunk upload requests:
      // chunkalways: function (e, data) {}, // .on('fileuploadchunkalways', func);

      // The plugin options are used as settings object for the ajax calls.
      // The following are jQuery ajax settings required for the file uploads:
      processData: false,
      contentType: false,
      cache: false,
      timeout: 0
    },

    // jQuery versions before 1.8 require promise.pipe if the return value is
    // used, as promise.then in older versions has a different behavior, see:
    // https://blog.jquery.com/2012/08/09/jquery-1-8-released/
    // https://bugs.jquery.com/ticket/11010
    // https://github.com/blueimp/jQuery-File-Upload/pull/3435
    _promisePipe: (function () {
      var parts = $.fn.jquery.split('.');
      return Number(parts[0]) > 1 || Number(parts[1]) > 7 ? 'then' : 'pipe';
    })(),

    // A list of options that require reinitializing event listeners and/or
    // special initialization code:
    _specialOptions: [
      'fileInput',
      'dropZone',
      'pasteZone',
      'multipart',
      'forceIframeTransport'
    ],

    _blobSlice:
      $.support.blobSlice &&
      function () {
        var slice = this.slice || this.webkitSlice || this.mozSlice;
        return slice.apply(this, arguments);
      },

    _BitrateTimer: function () {
      this.timestamp = Date.now ? Date.now() : new Date().getTime();
      this.loaded = 0;
      this.bitrate = 0;
      this.getBitrate = function (now, loaded, interval) {
        var timeDiff = now - this.timestamp;
        if (!this.bitrate || !interval || timeDiff > interval) {
          this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
          this.loaded = loaded;
          this.timestamp = now;
        }
        return this.bitrate;
      };
    },

    _isXHRUpload: function (options) {
      return (
        !options.forceIframeTransport &&
        ((!options.multipart && $.support.xhrFileUpload) ||
          $.support.xhrFormDataFileUpload)
      );
    },

    _getFormData: function (options) {
      var formData;
      if ($.type(options.formData) === 'function') {
        return options.formData(options.form);
      }
      if ($.isArray(options.formData)) {
        return options.formData;
      }
      if ($.type(options.formData) === 'object') {
        formData = [];
        $.each(options.formData, function (name, value) {
          formData.push({ name: name, value: value });
        });
        return formData;
      }
      return [];
    },

    _getTotal: function (files) {
      var total = 0;
      $.each(files, function (index, file) {
        total += file.size || 1;
      });
      return total;
    },

    _initProgressObject: function (obj) {
      var progress = {
        loaded: 0,
        total: 0,
        bitrate: 0
      };
      if (obj._progress) {
        $.extend(obj._progress, progress);
      } else {
        obj._progress = progress;
      }
    },

    _initResponseObject: function (obj) {
      var prop;
      if (obj._response) {
        for (prop in obj._response) {
          if (Object.prototype.hasOwnProperty.call(obj._response, prop)) {
            delete obj._response[prop];
          }
        }
      } else {
        obj._response = {};
      }
    },

    _onProgress: function (e, data) {
      if (e.lengthComputable) {
        var now = Date.now ? Date.now() : new Date().getTime(),
          loaded;
        if (
          data._time &&
          data.progressInterval &&
          now - data._time < data.progressInterval &&
          e.loaded !== e.total
        ) {
          return;
        }
        data._time = now;
        loaded =
          Math.floor(
            (e.loaded / e.total) * (data.chunkSize || data._progress.total)
          ) + (data.uploadedBytes || 0);
        // Add the difference from the previously loaded state
        // to the global loaded counter:
        this._progress.loaded += loaded - data._progress.loaded;
        this._progress.bitrate = this._bitrateTimer.getBitrate(
          now,
          this._progress.loaded,
          data.bitrateInterval
        );
        data._progress.loaded = data.loaded = loaded;
        data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
          now,
          loaded,
          data.bitrateInterval
        );
        // Trigger a custom progress event with a total data property set
        // to the file size(s) of the current upload and a loaded data
        // property calculated accordingly:
        this._trigger(
          'progress',
          $.Event('progress', { delegatedEvent: e }),
          data
        );
        // Trigger a global progress event for all current file uploads,
        // including ajax calls queued for sequential file uploads:
        this._trigger(
          'progressall',
          $.Event('progressall', { delegatedEvent: e }),
          this._progress
        );
      }
    },

    _initProgressListener: function (options) {
      var that = this,
        xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
      // Access to the native XHR object is required to add event listeners
      // for the upload progress event:
      if (xhr.upload) {
        $(xhr.upload).on('progress', function (e) {
          var oe = e.originalEvent;
          // Make sure the progress event properties get copied over:
          e.lengthComputable = oe.lengthComputable;
          e.loaded = oe.loaded;
          e.total = oe.total;
          that._onProgress(e, options);
        });
        options.xhr = function () {
          return xhr;
        };
      }
    },

    _deinitProgressListener: function (options) {
      var xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
      if (xhr.upload) {
        $(xhr.upload).off('progress');
      }
    },

    _isInstanceOf: function (type, obj) {
      // Cross-frame instanceof check
      return Object.prototype.toString.call(obj) === '[object ' + type + ']';
    },

    _getUniqueFilename: function (name, map) {
      // eslint-disable-next-line no-param-reassign
      name = String(name);
      if (map[name]) {
        // eslint-disable-next-line no-param-reassign
        name = name.replace(
          /(?: \(([\d]+)\))?(\.[^.]+)?$/,
          function (_, p1, p2) {
            var index = p1 ? Number(p1) + 1 : 1;
            var ext = p2 || '';
            return ' (' + index + ')' + ext;
          }
        );
        return this._getUniqueFilename(name, map);
      }
      map[name] = true;
      return name;
    },

    _initXHRData: function (options) {
      var that = this,
        formData,
        file = options.files[0],
        // Ignore non-multipart setting if not supported:
        multipart = options.multipart || !$.support.xhrFileUpload,
        paramName =
          $.type(options.paramName) === 'array'
            ? options.paramName[0]
            : options.paramName;
      options.headers = $.extend({}, options.headers);
      if (options.contentRange) {
        options.headers['Content-Range'] = options.contentRange;
      }
      if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
        options.headers['Content-Disposition'] =
          'attachment; filename="' +
          encodeURI(file.uploadName || file.name) +
          '"';
      }
      if (!multipart) {
        options.contentType = file.type || 'application/octet-stream';
        options.data = options.blob || file;
      } else if ($.support.xhrFormDataFileUpload) {
        if (options.postMessage) {
          // window.postMessage does not allow sending FormData
          // objects, so we just add the File/Blob objects to
          // the formData array and let the postMessage window
          // create the FormData object out of this array:
          formData = this._getFormData(options);
          if (options.blob) {
            formData.push({
              name: paramName,
              value: options.blob
            });
          } else {
            $.each(options.files, function (index, file) {
              formData.push({
                name:
                  ($.type(options.paramName) === 'array' &&
                    options.paramName[index]) ||
                  paramName,
                value: file
              });
            });
          }
        } else {
          if (that._isInstanceOf('FormData', options.formData)) {
            formData = options.formData;
          } else {
            formData = new FormData();
            $.each(this._getFormData(options), function (index, field) {
              formData.append(field.name, field.value);
            });
          }
          if (options.blob) {
            formData.append(
              paramName,
              options.blob,
              file.uploadName || file.name
            );
          } else {
            $.each(options.files, function (index, file) {
              // This check allows the tests to run with
              // dummy objects:
              if (
                that._isInstanceOf('File', file) ||
                that._isInstanceOf('Blob', file)
              ) {
                var fileName = file.uploadName || file.name;
                if (options.uniqueFilenames) {
                  fileName = that._getUniqueFilename(
                    fileName,
                    options.uniqueFilenames
                  );
                }
                formData.append(
                  ($.type(options.paramName) === 'array' &&
                    options.paramName[index]) ||
                    paramName,
                  file,
                  fileName
                );
              }
            });
          }
        }
        options.data = formData;
      }
      // Blob reference is not needed anymore, free memory:
      options.blob = null;
    },

    _initIframeSettings: function (options) {
      var targetHost = $('<a></a>').prop('href', options.url).prop('host');
      // Setting the dataType to iframe enables the iframe transport:
      options.dataType = 'iframe ' + (options.dataType || '');
      // The iframe transport accepts a serialized array as form data:
      options.formData = this._getFormData(options);
      // Add redirect url to form data on cross-domain uploads:
      if (options.redirect && targetHost && targetHost !== location.host) {
        options.formData.push({
          name: options.redirectParamName || 'redirect',
          value: options.redirect
        });
      }
    },

    _initDataSettings: function (options) {
      if (this._isXHRUpload(options)) {
        if (!this._chunkedUpload(options, true)) {
          if (!options.data) {
            this._initXHRData(options);
          }
          this._initProgressListener(options);
        }
        if (options.postMessage) {
          // Setting the dataType to postmessage enables the
          // postMessage transport:
          options.dataType = 'postmessage ' + (options.dataType || '');
        }
      } else {
        this._initIframeSettings(options);
      }
    },

    _getParamName: function (options) {
      var fileInput = $(options.fileInput),
        paramName = options.paramName;
      if (!paramName) {
        paramName = [];
        fileInput.each(function () {
          var input = $(this),
            name = input.prop('name') || 'files[]',
            i = (input.prop('files') || [1]).length;
          while (i) {
            paramName.push(name);
            i -= 1;
          }
        });
        if (!paramName.length) {
          paramName = [fileInput.prop('name') || 'files[]'];
        }
      } else if (!$.isArray(paramName)) {
        paramName = [paramName];
      }
      return paramName;
    },

    _initFormSettings: function (options) {
      // Retrieve missing options from the input field and the
      // associated form, if available:
      if (!options.form || !options.form.length) {
        options.form = $(options.fileInput.prop('form'));
        // If the given file input doesn't have an associated form,
        // use the default widget file input's form:
        if (!options.form.length) {
          options.form = $(this.options.fileInput.prop('form'));
        }
      }
      options.paramName = this._getParamName(options);
      if (!options.url) {
        options.url = options.form.prop('action') || location.href;
      }
      // The HTTP request method must be "POST" or "PUT":
      options.type = (
        options.type ||
        ($.type(options.form.prop('method')) === 'string' &&
          options.form.prop('method')) ||
        ''
      ).toUpperCase();
      if (
        options.type !== 'POST' &&
        options.type !== 'PUT' &&
        options.type !== 'PATCH'
      ) {
        options.type = 'POST';
      }
      if (!options.formAcceptCharset) {
        options.formAcceptCharset = options.form.attr('accept-charset');
      }
    },

    _getAJAXSettings: function (data) {
      var options = $.extend({}, this.options, data);
      this._initFormSettings(options);
      this._initDataSettings(options);
      return options;
    },

    // jQuery 1.6 doesn't provide .state(),
    // while jQuery 1.8+ removed .isRejected() and .isResolved():
    _getDeferredState: function (deferred) {
      if (deferred.state) {
        return deferred.state();
      }
      if (deferred.isResolved()) {
        return 'resolved';
      }
      if (deferred.isRejected()) {
        return 'rejected';
      }
      return 'pending';
    },

    // Maps jqXHR callbacks to the equivalent
    // methods of the given Promise object:
    _enhancePromise: function (promise) {
      promise.success = promise.done;
      promise.error = promise.fail;
      promise.complete = promise.always;
      return promise;
    },

    // Creates and returns a Promise object enhanced with
    // the jqXHR methods abort, success, error and complete:
    _getXHRPromise: function (resolveOrReject, context, args) {
      var dfd = $.Deferred(),
        promise = dfd.promise();
      // eslint-disable-next-line no-param-reassign
      context = context || this.options.context || promise;
      if (resolveOrReject === true) {
        dfd.resolveWith(context, args);
      } else if (resolveOrReject === false) {
        dfd.rejectWith(context, args);
      }
      promise.abort = dfd.promise;
      return this._enhancePromise(promise);
    },

    // Adds convenience methods to the data callback argument:
    _addConvenienceMethods: function (e, data) {
      var that = this,
        getPromise = function (args) {
          return $.Deferred().resolveWith(that, args).promise();
        };
      data.process = function (resolveFunc, rejectFunc) {
        if (resolveFunc || rejectFunc) {
          data._processQueue = this._processQueue = (this._processQueue ||
            getPromise([this]))
            [that._promisePipe](function () {
              if (data.errorThrown) {
                return $.Deferred().rejectWith(that, [data]).promise();
              }
              return getPromise(arguments);
            })
            [that._promisePipe](resolveFunc, rejectFunc);
        }
        return this._processQueue || getPromise([this]);
      };
      data.submit = function () {
        if (this.state() !== 'pending') {
          data.jqXHR = this.jqXHR =
            that._trigger(
              'submit',
              $.Event('submit', { delegatedEvent: e }),
              this
            ) !== false && that._onSend(e, this);
        }
        return this.jqXHR || that._getXHRPromise();
      };
      data.abort = function () {
        if (this.jqXHR) {
          return this.jqXHR.abort();
        }
        this.errorThrown = 'abort';
        that._trigger('fail', null, this);
        return that._getXHRPromise(false);
      };
      data.state = function () {
        if (this.jqXHR) {
          return that._getDeferredState(this.jqXHR);
        }
        if (this._processQueue) {
          return that._getDeferredState(this._processQueue);
        }
      };
      data.processing = function () {
        return (
          !this.jqXHR &&
          this._processQueue &&
          that._getDeferredState(this._processQueue) === 'pending'
        );
      };
      data.progress = function () {
        return this._progress;
      };
      data.response = function () {
        return this._response;
      };
    },

    // Parses the Range header from the server response
    // and returns the uploaded bytes:
    _getUploadedBytes: function (jqXHR) {
      var range = jqXHR.getResponseHeader('Range'),
        parts = range && range.split('-'),
        upperBytesPos = parts && parts.length > 1 && parseInt(parts[1], 10);
      return upperBytesPos && upperBytesPos + 1;
    },

    // Uploads a file in multiple, sequential requests
    // by splitting the file up in multiple blob chunks.
    // If the second parameter is true, only tests if the file
    // should be uploaded in chunks, but does not invoke any
    // upload requests:
    _chunkedUpload: function (options, testOnly) {
      options.uploadedBytes = options.uploadedBytes || 0;
      var that = this,
        file = options.files[0],
        fs = file.size,
        ub = options.uploadedBytes,
        mcs = options.maxChunkSize || fs,
        slice = this._blobSlice,
        dfd = $.Deferred(),
        promise = dfd.promise(),
        jqXHR,
        upload;
      if (
        !(
          this._isXHRUpload(options) &&
          slice &&
          (ub || ($.type(mcs) === 'function' ? mcs(options) : mcs) < fs)
        ) ||
        options.data
      ) {
        return false;
      }
      if (testOnly) {
        return true;
      }
      if (ub >= fs) {
        file.error = options.i18n('uploadedBytes');
        return this._getXHRPromise(false, options.context, [
          null,
          'error',
          file.error
        ]);
      }
      // The chunk upload method:
      upload = function () {
        // Clone the options object for each chunk upload:
        var o = $.extend({}, options),
          currentLoaded = o._progress.loaded;
        o.blob = slice.call(
          file,
          ub,
          ub + ($.type(mcs) === 'function' ? mcs(o) : mcs),
          file.type
        );
        // Store the current chunk size, as the blob itself
        // will be dereferenced after data processing:
        o.chunkSize = o.blob.size;
        // Expose the chunk bytes position range:
        o.contentRange =
          'bytes ' + ub + '-' + (ub + o.chunkSize - 1) + '/' + fs;
        // Trigger chunkbeforesend to allow form data to be updated for this chunk
        that._trigger('chunkbeforesend', null, o);
        // Process the upload data (the blob and potential form data):
        that._initXHRData(o);
        // Add progress listeners for this chunk upload:
        that._initProgressListener(o);
        jqXHR = (
          (that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
          that._getXHRPromise(false, o.context)
        )
          .done(function (result, textStatus, jqXHR) {
            ub = that._getUploadedBytes(jqXHR) || ub + o.chunkSize;
            // Create a progress event if no final progress event
            // with loaded equaling total has been triggered
            // for this chunk:
            if (currentLoaded + o.chunkSize - o._progress.loaded) {
              that._onProgress(
                $.Event('progress', {
                  lengthComputable: true,
                  loaded: ub - o.uploadedBytes,
                  total: ub - o.uploadedBytes
                }),
                o
              );
            }
            options.uploadedBytes = o.uploadedBytes = ub;
            o.result = result;
            o.textStatus = textStatus;
            o.jqXHR = jqXHR;
            that._trigger('chunkdone', null, o);
            that._trigger('chunkalways', null, o);
            if (ub < fs) {
              // File upload not yet complete,
              // continue with the next chunk:
              upload();
            } else {
              dfd.resolveWith(o.context, [result, textStatus, jqXHR]);
            }
          })
          .fail(function (jqXHR, textStatus, errorThrown) {
            o.jqXHR = jqXHR;
            o.textStatus = textStatus;
            o.errorThrown = errorThrown;
            that._trigger('chunkfail', null, o);
            that._trigger('chunkalways', null, o);
            dfd.rejectWith(o.context, [jqXHR, textStatus, errorThrown]);
          })
          .always(function () {
            that._deinitProgressListener(o);
          });
      };
      this._enhancePromise(promise);
      promise.abort = function () {
        return jqXHR.abort();
      };
      upload();
      return promise;
    },

    _beforeSend: function (e, data) {
      if (this._active === 0) {
        // the start callback is triggered when an upload starts
        // and no other uploads are currently running,
        // equivalent to the global ajaxStart event:
        this._trigger('start');
        // Set timer for global bitrate progress calculation:
        this._bitrateTimer = new this._BitrateTimer();
        // Reset the global progress values:
        this._progress.loaded = this._progress.total = 0;
        this._progress.bitrate = 0;
      }
      // Make sure the container objects for the .response() and
      // .progress() methods on the data object are available
      // and reset to their initial state:
      this._initResponseObject(data);
      this._initProgressObject(data);
      data._progress.loaded = data.loaded = data.uploadedBytes || 0;
      data._progress.total = data.total = this._getTotal(data.files) || 1;
      data._progress.bitrate = data.bitrate = 0;
      this._active += 1;
      // Initialize the global progress values:
      this._progress.loaded += data.loaded;
      this._progress.total += data.total;
    },

    _onDone: function (result, textStatus, jqXHR, options) {
      var total = options._progress.total,
        response = options._response;
      if (options._progress.loaded < total) {
        // Create a progress event if no final progress event
        // with loaded equaling total has been triggered:
        this._onProgress(
          $.Event('progress', {
            lengthComputable: true,
            loaded: total,
            total: total
          }),
          options
        );
      }
      response.result = options.result = result;
      response.textStatus = options.textStatus = textStatus;
      response.jqXHR = options.jqXHR = jqXHR;
      this._trigger('done', null, options);
    },

    _onFail: function (jqXHR, textStatus, errorThrown, options) {
      var response = options._response;
      if (options.recalculateProgress) {
        // Remove the failed (error or abort) file upload from
        // the global progress calculation:
        this._progress.loaded -= options._progress.loaded;
        this._progress.total -= options._progress.total;
      }
      response.jqXHR = options.jqXHR = jqXHR;
      response.textStatus = options.textStatus = textStatus;
      response.errorThrown = options.errorThrown = errorThrown;
      this._trigger('fail', null, options);
    },

    _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
      // jqXHRorResult, textStatus and jqXHRorError are added to the
      // options object via done and fail callbacks
      this._trigger('always', null, options);
    },

    _onSend: function (e, data) {
      if (!data.submit) {
        this._addConvenienceMethods(e, data);
      }
      var that = this,
        jqXHR,
        aborted,
        slot,
        pipe,
        options = that._getAJAXSettings(data),
        send = function () {
          that._sending += 1;
          // Set timer for bitrate progress calculation:
          options._bitrateTimer = new that._BitrateTimer();
          jqXHR =
            jqXHR ||
            (
              ((aborted ||
                that._trigger(
                  'send',
                  $.Event('send', { delegatedEvent: e }),
                  options
                ) === false) &&
                that._getXHRPromise(false, options.context, aborted)) ||
              that._chunkedUpload(options) ||
              $.ajax(options)
            )
              .done(function (result, textStatus, jqXHR) {
                that._onDone(result, textStatus, jqXHR, options);
              })
              .fail(function (jqXHR, textStatus, errorThrown) {
                that._onFail(jqXHR, textStatus, errorThrown, options);
              })
              .always(function (jqXHRorResult, textStatus, jqXHRorError) {
                that._deinitProgressListener(options);
                that._onAlways(
                  jqXHRorResult,
                  textStatus,
                  jqXHRorError,
                  options
                );
                that._sending -= 1;
                that._active -= 1;
                if (
                  options.limitConcurrentUploads &&
                  options.limitConcurrentUploads > that._sending
                ) {
                  // Start the next queued upload,
                  // that has not been aborted:
                  var nextSlot = that._slots.shift();
                  while (nextSlot) {
                    if (that._getDeferredState(nextSlot) === 'pending') {
                      nextSlot.resolve();
                      break;
                    }
                    nextSlot = that._slots.shift();
                  }
                }
                if (that._active === 0) {
                  // The stop callback is triggered when all uploads have
                  // been completed, equivalent to the global ajaxStop event:
                  that._trigger('stop');
                }
              });
          return jqXHR;
        };
      this._beforeSend(e, options);
      if (
        this.options.sequentialUploads ||
        (this.options.limitConcurrentUploads &&
          this.options.limitConcurrentUploads <= this._sending)
      ) {
        if (this.options.limitConcurrentUploads > 1) {
          slot = $.Deferred();
          this._slots.push(slot);
          pipe = slot[that._promisePipe](send);
        } else {
          this._sequence = this._sequence[that._promisePipe](send, send);
          pipe = this._sequence;
        }
        // Return the piped Promise object, enhanced with an abort method,
        // which is delegated to the jqXHR object of the current upload,
        // and jqXHR callbacks mapped to the equivalent Promise methods:
        pipe.abort = function () {
          aborted = [undefined, 'abort', 'abort'];
          if (!jqXHR) {
            if (slot) {
              slot.rejectWith(options.context, aborted);
            }
            return send();
          }
          return jqXHR.abort();
        };
        return this._enhancePromise(pipe);
      }
      return send();
    },

    _onAdd: function (e, data) {
      var that = this,
        result = true,
        options = $.extend({}, this.options, data),
        files = data.files,
        filesLength = files.length,
        limit = options.limitMultiFileUploads,
        limitSize = options.limitMultiFileUploadSize,
        overhead = options.limitMultiFileUploadSizeOverhead,
        batchSize = 0,
        paramName = this._getParamName(options),
        paramNameSet,
        paramNameSlice,
        fileSet,
        i,
        j = 0;
      if (!filesLength) {
        return false;
      }
      if (limitSize && files[0].size === undefined) {
        limitSize = undefined;
      }
      if (
        !(options.singleFileUploads || limit || limitSize) ||
        !this._isXHRUpload(options)
      ) {
        fileSet = [files];
        paramNameSet = [paramName];
      } else if (!(options.singleFileUploads || limitSize) && limit) {
        fileSet = [];
        paramNameSet = [];
        for (i = 0; i < filesLength; i += limit) {
          fileSet.push(files.slice(i, i + limit));
          paramNameSlice = paramName.slice(i, i + limit);
          if (!paramNameSlice.length) {
            paramNameSlice = paramName;
          }
          paramNameSet.push(paramNameSlice);
        }
      } else if (!options.singleFileUploads && limitSize) {
        fileSet = [];
        paramNameSet = [];
        for (i = 0; i < filesLength; i = i + 1) {
          batchSize += files[i].size + overhead;
          if (
            i + 1 === filesLength ||
            batchSize + files[i + 1].size + overhead > limitSize ||
            (limit && i + 1 - j >= limit)
          ) {
            fileSet.push(files.slice(j, i + 1));
            paramNameSlice = paramName.slice(j, i + 1);
            if (!paramNameSlice.length) {
              paramNameSlice = paramName;
            }
            paramNameSet.push(paramNameSlice);
            j = i + 1;
            batchSize = 0;
          }
        }
      } else {
        paramNameSet = paramName;
      }
      data.originalFiles = files;
      $.each(fileSet || files, function (index, element) {
        var newData = $.extend({}, data);
        newData.files = fileSet ? element : [element];
        newData.paramName = paramNameSet[index];
        that._initResponseObject(newData);
        that._initProgressObject(newData);
        that._addConvenienceMethods(e, newData);
        result = that._trigger(
          'add',
          $.Event('add', { delegatedEvent: e }),
          newData
        );
        return result;
      });
      return result;
    },

    _replaceFileInput: function (data) {
      var input = data.fileInput,
        inputClone = input.clone(true),
        restoreFocus = input.is(document.activeElement);
      // Add a reference for the new cloned file input to the data argument:
      data.fileInputClone = inputClone;
      $('<form></form>').append(inputClone)[0].reset();
      // Detaching allows to insert the fileInput on another form
      // without losing the file input value:
      input.after(inputClone).detach();
      // If the fileInput had focus before it was detached,
      // restore focus to the inputClone.
      if (restoreFocus) {
        inputClone.trigger('focus');
      }
      // Avoid memory leaks with the detached file input:
      $.cleanData(input.off('remove'));
      // Replace the original file input element in the fileInput
      // elements set with the clone, which has been copied including
      // event handlers:
      this.options.fileInput = this.options.fileInput.map(function (i, el) {
        if (el === input[0]) {
          return inputClone[0];
        }
        return el;
      });
      // If the widget has been initialized on the file input itself,
      // override this.element with the file input clone:
      if (input[0] === this.element[0]) {
        this.element = inputClone;
      }
    },

    _handleFileTreeEntry: function (entry, path) {
      var that = this,
        dfd = $.Deferred(),
        entries = [],
        dirReader,
        errorHandler = function (e) {
          if (e && !e.entry) {
            e.entry = entry;
          }
          // Since $.when returns immediately if one
          // Deferred is rejected, we use resolve instead.
          // This allows valid files and invalid items
          // to be returned together in one set:
          dfd.resolve([e]);
        },
        successHandler = function (entries) {
          that
            ._handleFileTreeEntries(entries, path + entry.name + '/')
            .done(function (files) {
              dfd.resolve(files);
            })
            .fail(errorHandler);
        },
        readEntries = function () {
          dirReader.readEntries(function (results) {
            if (!results.length) {
              successHandler(entries);
            } else {
              entries = entries.concat(results);
              readEntries();
            }
          }, errorHandler);
        };
      // eslint-disable-next-line no-param-reassign
      path = path || '';
      if (entry.isFile) {
        if (entry._file) {
          // Workaround for Chrome bug #149735
          entry._file.relativePath = path;
          dfd.resolve(entry._file);
        } else {
          entry.file(function (file) {
            file.relativePath = path;
            dfd.resolve(file);
          }, errorHandler);
        }
      } else if (entry.isDirectory) {
        dirReader = entry.createReader();
        readEntries();
      } else {
        // Return an empty list for file system items
        // other than files or directories:
        dfd.resolve([]);
      }
      return dfd.promise();
    },

    _handleFileTreeEntries: function (entries, path) {
      var that = this;
      return $.when
        .apply(
          $,
          $.map(entries, function (entry) {
            return that._handleFileTreeEntry(entry, path);
          })
        )
        [this._promisePipe](function () {
          return Array.prototype.concat.apply([], arguments);
        });
    },

    _getDroppedFiles: function (dataTransfer) {
      // eslint-disable-next-line no-param-reassign
      dataTransfer = dataTransfer || {};
      var items = dataTransfer.items;
      if (
        items &&
        items.length &&
        (items[0].webkitGetAsEntry || items[0].getAsEntry)
      ) {
        return this._handleFileTreeEntries(
          $.map(items, function (item) {
            var entry;
            if (item.webkitGetAsEntry) {
              entry = item.webkitGetAsEntry();
              if (entry) {
                // Workaround for Chrome bug #149735:
                entry._file = item.getAsFile();
              }
              return entry;
            }
            return item.getAsEntry();
          })
        );
      }
      return $.Deferred().resolve($.makeArray(dataTransfer.files)).promise();
    },

    _getSingleFileInputFiles: function (fileInput) {
      // eslint-disable-next-line no-param-reassign
      fileInput = $(fileInput);
      var entries = fileInput.prop('entries'),
        files,
        value;
      if (entries && entries.length) {
        return this._handleFileTreeEntries(entries);
      }
      files = $.makeArray(fileInput.prop('files'));
      if (!files.length) {
        value = fileInput.prop('value');
        if (!value) {
          return $.Deferred().resolve([]).promise();
        }
        // If the files property is not available, the browser does not
        // support the File API and we add a pseudo File object with
        // the input value as name with path information removed:
        files = [{ name: value.replace(/^.*\\/, '') }];
      } else if (files[0].name === undefined && files[0].fileName) {
        // File normalization for Safari 4 and Firefox 3:
        $.each(files, function (index, file) {
          file.name = file.fileName;
          file.size = file.fileSize;
        });
      }
      return $.Deferred().resolve(files).promise();
    },

    _getFileInputFiles: function (fileInput) {
      if (!(fileInput instanceof $) || fileInput.length === 1) {
        return this._getSingleFileInputFiles(fileInput);
      }
      return $.when
        .apply($, $.map(fileInput, this._getSingleFileInputFiles))
        [this._promisePipe](function () {
          return Array.prototype.concat.apply([], arguments);
        });
    },

    _onChange: function (e) {
      var that = this,
        data = {
          fileInput: $(e.target),
          form: $(e.target.form)
        };
      this._getFileInputFiles(data.fileInput).always(function (files) {
        data.files = files;
        if (that.options.replaceFileInput) {
          that._replaceFileInput(data);
        }
        if (
          that._trigger(
            'change',
            $.Event('change', { delegatedEvent: e }),
            data
          ) !== false
        ) {
          that._onAdd(e, data);
        }
      });
    },

    _onPaste: function (e) {
      var items =
          e.originalEvent &&
          e.originalEvent.clipboardData &&
          e.originalEvent.clipboardData.items,
        data = { files: [] };
      if (items && items.length) {
        $.each(items, function (index, item) {
          var file = item.getAsFile && item.getAsFile();
          if (file) {
            data.files.push(file);
          }
        });
        if (
          this._trigger(
            'paste',
            $.Event('paste', { delegatedEvent: e }),
            data
          ) !== false
        ) {
          this._onAdd(e, data);
        }
      }
    },

    _onDrop: function (e) {
      e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
      var that = this,
        dataTransfer = e.dataTransfer,
        data = {};
      if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
        e.preventDefault();
        this._getDroppedFiles(dataTransfer).always(function (files) {
          data.files = files;
          if (
            that._trigger(
              'drop',
              $.Event('drop', { delegatedEvent: e }),
              data
            ) !== false
          ) {
            that._onAdd(e, data);
          }
        });
      }
    },

    _onDragOver: getDragHandler('dragover'),

    _onDragEnter: getDragHandler('dragenter'),

    _onDragLeave: getDragHandler('dragleave'),

    _initEventHandlers: function () {
      if (this._isXHRUpload(this.options)) {
        this._on(this.options.dropZone, {
          dragover: this._onDragOver,
          drop: this._onDrop,
          // event.preventDefault() on dragenter is required for IE10+:
          dragenter: this._onDragEnter,
          // dragleave is not required, but added for completeness:
          dragleave: this._onDragLeave
        });
        this._on(this.options.pasteZone, {
          paste: this._onPaste
        });
      }
      if ($.support.fileInput) {
        this._on(this.options.fileInput, {
          change: this._onChange
        });
      }
    },

    _destroyEventHandlers: function () {
      this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
      this._off(this.options.pasteZone, 'paste');
      this._off(this.options.fileInput, 'change');
    },

    _destroy: function () {
      this._destroyEventHandlers();
    },

    _setOption: function (key, value) {
      var reinit = $.inArray(key, this._specialOptions) !== -1;
      if (reinit) {
        this._destroyEventHandlers();
      }
      this._super(key, value);
      if (reinit) {
        this._initSpecialOptions();
        this._initEventHandlers();
      }
    },

    _initSpecialOptions: function () {
      var options = this.options;
      if (options.fileInput === undefined) {
        options.fileInput = this.element.is('input[type="file"]')
          ? this.element
          : this.element.find('input[type="file"]');
      } else if (!(options.fileInput instanceof $)) {
        options.fileInput = $(options.fileInput);
      }
      if (!(options.dropZone instanceof $)) {
        options.dropZone = $(options.dropZone);
      }
      if (!(options.pasteZone instanceof $)) {
        options.pasteZone = $(options.pasteZone);
      }
    },

    _getRegExp: function (str) {
      var parts = str.split('/'),
        modifiers = parts.pop();
      parts.shift();
      return new RegExp(parts.join('/'), modifiers);
    },

    _isRegExpOption: function (key, value) {
      return (
        key !== 'url' &&
        $.type(value) === 'string' &&
        /^\/.*\/[igm]{0,3}$/.test(value)
      );
    },

    _initDataAttributes: function () {
      var that = this,
        options = this.options,
        data = this.element.data();
      // Initialize options set via HTML5 data-attributes:
      $.each(this.element[0].attributes, function (index, attr) {
        var key = attr.name.toLowerCase(),
          value;
        if (/^data-/.test(key)) {
          // Convert hyphen-ated key to camelCase:
          key = key.slice(5).replace(/-[a-z]/g, function (str) {
            return str.charAt(1).toUpperCase();
          });
          value = data[key];
          if (that._isRegExpOption(key, value)) {
            value = that._getRegExp(value);
          }
          options[key] = value;
        }
      });
    },

    _create: function () {
      this._initDataAttributes();
      this._initSpecialOptions();
      this._slots = [];
      this._sequence = this._getXHRPromise(true);
      this._sending = this._active = 0;
      this._initProgressObject(this);
      this._initEventHandlers();
    },

    // This method is exposed to the widget API and allows to query
    // the number of active uploads:
    active: function () {
      return this._active;
    },

    // This method is exposed to the widget API and allows to query
    // the widget upload progress.
    // It returns an object with loaded, total and bitrate properties
    // for the running uploads:
    progress: function () {
      return this._progress;
    },

    // This method is exposed to the widget API and allows adding files
    // using the fileupload API. The data parameter accepts an object which
    // must have a files property and can contain additional options:
    // .fileupload('add', {files: filesList});
    add: function (data) {
      var that = this;
      if (!data || this.options.disabled) {
        return;
      }
      if (data.fileInput && !data.files) {
        this._getFileInputFiles(data.fileInput).always(function (files) {
          data.files = files;
          that._onAdd(null, data);
        });
      } else {
        data.files = $.makeArray(data.files);
        this._onAdd(null, data);
      }
    },

    // This method is exposed to the widget API and allows sending files
    // using the fileupload API. The data parameter accepts an object which
    // must have a files or fileInput property and can contain additional options:
    // .fileupload('send', {files: filesList});
    // The method returns a Promise object for the file upload call.
    send: function (data) {
      if (data && !this.options.disabled) {
        if (data.fileInput && !data.files) {
          var that = this,
            dfd = $.Deferred(),
            promise = dfd.promise(),
            jqXHR,
            aborted;
          promise.abort = function () {
            aborted = true;
            if (jqXHR) {
              return jqXHR.abort();
            }
            dfd.reject(null, 'abort', 'abort');
            return promise;
          };
          this._getFileInputFiles(data.fileInput).always(function (files) {
            if (aborted) {
              return;
            }
            if (!files.length) {
              dfd.reject();
              return;
            }
            data.files = files;
            jqXHR = that._onSend(null, data);
            jqXHR.then(
              function (result, textStatus, jqXHR) {
                dfd.resolve(result, textStatus, jqXHR);
              },
              function (jqXHR, textStatus, errorThrown) {
                dfd.reject(jqXHR, textStatus, errorThrown);
              }
            );
          });
          return this._enhancePromise(promise);
        }
        data.files = $.makeArray(data.files);
        if (data.files.length) {
          return this._onSend(null, data);
        }
      }
      return this._getXHRPromise(false, data && data.context);
    }
  });
});


================================================
FILE: js/jquery.iframe-transport.js
================================================
/*
 * jQuery Iframe Transport Plugin
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * https://opensource.org/licenses/MIT
 */

/* global define, require */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // Register as an anonymous AMD module:
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS:
    factory(require('jquery'));
  } else {
    // Browser globals:
    factory(window.jQuery);
  }
})(function ($) {
  'use strict';

  // Helper variable to create unique names for the transport iframes:
  var counter = 0,
    jsonAPI = $,
    jsonParse = 'parseJSON';

  if ('JSON' in window && 'parse' in JSON) {
    jsonAPI = JSON;
    jsonParse = 'parse';
  }

  // The iframe transport accepts four additional options:
  // options.fileInput: a jQuery collection of file input fields
  // options.paramName: the parameter name for the file form data,
  //  overrides the name property of the file input field(s),
  //  can be a string or an array of strings.
  // options.formData: an array of objects with name and value properties,
  //  equivalent to the return data of .serializeArray(), e.g.:
  //  [{name: 'a', value: 1}, {name: 'b', value: 2}]
  // options.initialIframeSrc: the URL of the initial iframe src,
  //  by default set to "javascript:false;"
  $.ajaxTransport('iframe', function (options) {
    if (options.async) {
      // javascript:false as initial iframe src
      // prevents warning popups on HTTPS in IE6:
      // eslint-disable-next-line no-script-url
      var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
        form,
        iframe,
        addParamChar;
      return {
        send: function (_, completeCallback) {
          form = $('<form style="display:none;"></form>');
          form.attr('accept-charset', options.formAcceptCharset);
          addParamChar = /\?/.test(options.url) ? '&' : '?';
          // XDomainRequest only supports GET and POST:
          if (options.type === 'DELETE') {
            options.url = options.url + addParamChar + '_method=DELETE';
            options.type = 'POST';
          } else if (options.type === 'PUT') {
            options.url = options.url + addParamChar + '_method=PUT';
            options.type = 'POST';
          } else if (options.type === 'PATCH') {
            options.url = options.url + addParamChar + '_method=PATCH';
            options.type = 'POST';
          }
          // IE versions below IE8 cannot set the name property of
          // elements that have already been added to the DOM,
          // so we set the name along with the iframe HTML markup:
          counter += 1;
          iframe = $(
            '<iframe src="' +
              initialIframeSrc +
              '" name="iframe-transport-' +
              counter +
              '"></iframe>'
          ).on('load', function () {
            var fileInputClones,
              paramNames = $.isArray(options.paramName)
                ? options.paramName
                : [options.paramName];
            iframe.off('load').on('load', function () {
              var response;
              // Wrap in a try/catch block to catch exceptions thrown
              // when trying to access cross-domain iframe contents:
              try {
                response = iframe.contents();
                // Google Chrome and Firefox do not throw an
                // exception when calling iframe.contents() on
                // cross-domain requests, so we unify the response:
                if (!response.length || !response[0].firstChild) {
                  throw new Error();
                }
              } catch (e) {
                response = undefined;
              }
              // The complete callback returns the
              // iframe content document as response object:
              completeCallback(200, 'success', { iframe: response });
              // Fix for IE endless progress bar activity bug
              // (happens on form submits to iframe targets):
              $('<iframe src="' + initialIframeSrc + '"></iframe>').appendTo(
                form
              );
              window.setTimeout(function () {
                // Removing the form in a setTimeout call
                // allows Chrome's developer tools to display
                // the response result
                form.remove();
              }, 0);
            });
            form
              .prop('target', iframe.prop('name'))
              .prop('action', options.url)
              .prop('method', options.type);
            if (options.formData) {
              $.each(options.formData, function (index, field) {
                $('<input type="hidden"/>')
                  .prop('name', field.name)
                  .val(field.value)
                  .appendTo(form);
              });
            }
            if (
              options.fileInput &&
              options.fileInput.length &&
              options.type === 'POST'
            ) {
              fileInputClones = options.fileInput.clone();
              // Insert a clone for each file input field:
              options.fileInput.after(function (index) {
                return fileInputClones[index];
              });
              if (options.paramName) {
                options.fileInput.each(function (index) {
                  $(this).prop('name', paramNames[index] || options.paramName);
                });
              }
              // Appending the file input fields to the hidden form
              // removes them from their original location:
              form
                .append(options.fileInput)
                .prop('enctype', 'multipart/form-data')
                // enctype must be set as encoding for IE:
                .prop('encoding', 'multipart/form-data');
              // Remove the HTML5 form attribute from the input(s):
              options.fileInput.removeAttr('form');
            }
            window.setTimeout(function () {
              // Submitting the form in a setTimeout call fixes an issue with
              // Safari 13 not triggering the iframe load event after resetting
              // the load event handler, see also:
              // https://github.com/blueimp/jQuery-File-Upload/issues/3633
              form.submit();
              // Insert the file input fields at their original location
              // by replacing the clones with the originals:
              if (fileInputClones && fileInputClones.length) {
                options.fileInput.each(function (index, input) {
                  var clone = $(fileInputClones[index]);
                  // Restore the original name and form properties:
                  $(input)
                    .prop('name', clone.prop('name'))
                    .attr('form', clone.attr('form'));
                  clone.replaceWith(input);
                });
              }
            }, 0);
          });
          form.append(iframe).appendTo(document.body);
        },
        abort: function () {
          if (iframe) {
            // javascript:false as iframe src aborts the request
            // and prevents warning popups on HTTPS in IE6.
            iframe.off('load').prop('src', initialIframeSrc);
          }
          if (form) {
            form.remove();
          }
        }
      };
    }
  });

  // The iframe transport returns the iframe content document as response.
  // The following adds converters from iframe to text, json, html, xml
  // and script.
  // Please note that the Content-Type for JSON responses has to be text/plain
  // or text/html, if the browser doesn't include application/json in the
  // Accept header, else IE will show a download dialog.
  // The Content-Type for XML responses on the other hand has to be always
  // application/xml or text/xml, so IE properly parses the XML response.
  // See also
  // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
  $.ajaxSetup({
    converters: {
      'iframe text': function (iframe) {
        return iframe && $(iframe[0].body).text();
      },
      'iframe json': function (iframe) {
        return iframe && jsonAPI[jsonParse]($(iframe[0].body).text());
      },
      'iframe html': function (iframe) {
        return iframe && $(iframe[0].body).html();
      },
      'iframe xml': function (iframe) {
        var xmlDoc = iframe && iframe[0];
        return xmlDoc && $.isXMLDoc(xmlDoc)
          ? xmlDoc
          : $.parseXML(
              (xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
                $(xmlDoc.body).html()
            );
      },
      'iframe script': function (iframe) {
        return iframe && $.globalEval($(iframe[0].body).text());
      }
    }
  });
});


================================================
FILE: js/vendor/jquery.ui.widget.js
================================================
/*! jQuery UI - v1.12.1+0b7246b6eeadfa9e2696e22f3230f6452f8129dc - 2020-02-20
 * http://jqueryui.com
 * Includes: widget.js
 * Copyright jQuery Foundation and other contributors; Licensed MIT */

/* global define, require */
/* eslint-disable no-param-reassign, new-cap, jsdoc/require-jsdoc */

(function (factory) {
  'use strict';
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    // Node/CommonJS
    factory(require('jquery'));
  } else {
    // Browser globals
    factory(window.jQuery);
  }
})(function ($) {
  ('use strict');

  $.ui = $.ui || {};

  $.ui.version = '1.12.1';

  /*!
   * jQuery UI Widget 1.12.1
   * http://jqueryui.com
   *
   * Copyright jQuery Foundation and other contributors
   * Released under the MIT license.
   * http://jquery.org/license
   */

  //>>label: Widget
  //>>group: Core
  //>>description: Provides a factory for creating stateful widgets with a common API.
  //>>docs: http://api.jqueryui.com/jQuery.widget/
  //>>demos: http://jqueryui.com/widget/

  // Support: jQuery 1.9.x or older
  // $.expr[ ":" ] is deprecated.
  if (!$.expr.pseudos) {
    $.expr.pseudos = $.expr[':'];
  }

  // Support: jQuery 1.11.x or older
  // $.unique has been renamed to $.uniqueSort
  if (!$.uniqueSort) {
    $.uniqueSort = $.unique;
  }

  var widgetUuid = 0;
  var widgetHasOwnProperty = Array.prototype.hasOwnProperty;
  var widgetSlice = Array.prototype.slice;

  $.cleanData = (function (orig) {
    return function (elems) {
      var events, elem, i;
      // eslint-disable-next-line eqeqeq
      for (i = 0; (elem = elems[i]) != null; i++) {
        // Only trigger remove when necessary to save time
        events = $._data(elem, 'events');
        if (events && events.remove) {
          $(elem).triggerHandler('remove');
        }
      }
      orig(elems);
    };
  })($.cleanData);

  $.widget = function (name, base, prototype) {
    var existingConstructor, constructor, basePrototype;

    // ProxiedPrototype allows the provided prototype to remain unmodified
    // so that it can be used as a mixin for multiple widgets (#8876)
    var proxiedPrototype = {};

    var namespace = name.split('.')[0];
    name = name.split('.')[1];
    var fullName = namespace + '-' + name;

    if (!prototype) {
      prototype = base;
      base = $.Widget;
    }

    if ($.isArray(prototype)) {
      prototype = $.extend.apply(null, [{}].concat(prototype));
    }

    // Create selector for plugin
    $.expr.pseudos[fullName.toLowerCase()] = function (elem) {
      return !!$.data(elem, fullName);
    };

    $[namespace] = $[namespace] || {};
    existingConstructor = $[namespace][name];
    constructor = $[namespace][name] = function (options, element) {
      // Allow instantiation without "new" keyword
      if (!this._createWidget) {
        return new constructor(options, element);
      }

      // Allow instantiation without initializing for simple inheritance
      // must use "new" keyword (the code above always passes args)
      if (arguments.length) {
        this._createWidget(options, element);
      }
    };

    // Extend with the existing constructor to carry over any static properties
    $.extend(constructor, existingConstructor, {
      version: prototype.version,

      // Copy the object used to create the prototype in case we need to
      // redefine the widget later
      _proto: $.extend({}, prototype),

      // Track widgets that inherit from this widget in case this widget is
      // redefined after a widget inherits from it
      _childConstructors: []
    });

    basePrototype = new base();

    // We need to make the options hash a property directly on the new instance
    // otherwise we'll modify the options hash on the prototype that we're
    // inheriting from
    basePrototype.options = $.widget.extend({}, basePrototype.options);
    $.each(prototype, function (prop, value) {
      if (!$.isFunction(value)) {
        proxiedPrototype[prop] = value;
        return;
      }
      proxiedPrototype[prop] = (function () {
        function _super() {
          return base.prototype[prop].apply(this, arguments);
        }

        function _superApply(args) {
          return base.prototype[prop].apply(this, args);
        }

        return function () {
          var __super = this._super;
          var __superApply = this._superApply;
          var returnValue;

          this._super = _super;
          this._superApply = _superApply;

          returnValue = value.apply(this, arguments);

          this._super = __super;
          this._superApply = __superApply;

          return returnValue;
        };
      })();
    });
    constructor.prototype = $.widget.extend(
      basePrototype,
      {
        // TODO: remove support for widgetEventPrefix
        // always use the name + a colon as the prefix, e.g., draggable:start
        // don't prefix for widgets that aren't DOM-based
        widgetEventPrefix: existingConstructor
          ? basePrototype.widgetEventPrefix || name
          : name
      },
      proxiedPrototype,
      {
        constructor: constructor,
        namespace: namespace,
        widgetName: name,
        widgetFullName: fullName
      }
    );

    // If this widget is being redefined then we need to find all widgets that
    // are inheriting from it and redefine all of them so that they inherit from
    // the new version of this widget. We're essentially trying to replace one
    // level in the prototype chain.
    if (existingConstructor) {
      $.each(existingConstructor._childConstructors, function (i, child) {
        var childPrototype = child.prototype;

        // Redefine the child widget using the same prototype that was
        // originally used, but inherit from the new version of the base
        $.widget(
          childPrototype.namespace + '.' + childPrototype.widgetName,
          constructor,
          child._proto
        );
      });

      // Remove the list of existing child constructors from the old constructor
      // so the old child constructors can be garbage collected
      delete existingConstructor._childConstructors;
    } else {
      base._childConstructors.push(constructor);
    }

    $.widget.bridge(name, constructor);

    return constructor;
  };

  $.widget.extend = function (target) {
    var input = widgetSlice.call(arguments, 1);
    var inputIndex = 0;
    var inputLength = input.length;
    var key;
    var value;

    for (; inputIndex < inputLength; inputIndex++) {
      for (key in input[inputIndex]) {
        value = input[inputIndex][key];
        if (
          widgetHasOwnProperty.call(input[inputIndex], key) &&
          value !== undefined
        ) {
          // Clone objects
          if ($.isPlainObject(value)) {
            target[key] = $.isPlainObject(target[key])
              ? $.widget.extend({}, target[key], value)
              : // Don't extend strings, arrays, etc. with objects
                $.widget.extend({}, value);

            // Copy everything else by reference
          } else {
            target[key] = value;
          }
        }
      }
    }
    return target;
  };

  $.widget.bridge = function (name, object) {
    var fullName = object.prototype.widgetFullName || name;
    $.fn[name] = function (options) {
      var isMethodCall = typeof options === 'string';
      var args = widgetSlice.call(arguments, 1);
      var returnValue = this;

      if (isMethodCall) {
        // If this is an empty collection, we need to have the instance method
        // return undefined instead of the jQuery instance
        if (!this.length && options === 'instance') {
          returnValue = undefined;
        } else {
          this.each(function () {
            var methodValue;
            var instance = $.data(this, fullName);

            if (options === 'instance') {
              returnValue = instance;
              return false;
            }

            if (!instance) {
              return $.error(
                'cannot call methods on ' +
                  name +
                  ' prior to initialization; ' +
                  "attempted to call method '" +
                  options +
                  "'"
              );
            }

            if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
              return $.error(
                "no such method '" +
                  options +
                  "' for " +
                  name +
                  ' widget instance'
              );
            }

            methodValue = instance[options].apply(instance, args);

            if (methodValue !== instance && methodValue !== undefined) {
              returnValue =
                methodValue && methodValue.jquery
                  ? returnValue.pushStack(methodValue.get())
                  : methodValue;
              return false;
            }
          });
        }
      } else {
        // Allow multiple hashes to be passed on init
        if (args.length) {
          options = $.widget.extend.apply(null, [options].concat(args));
        }

        this.each(function () {
          var instance = $.data(this, fullName);
          if (instance) {
            instance.option(options || {});
            if (instance._init) {
              instance._init();
            }
          } else {
            $.data(this, fullName, new object(options, this));
          }
        });
      }

      return returnValue;
    };
  };

  $.Widget = function (/* options, element */) {};
  $.Widget._childConstructors = [];

  $.Widget.prototype = {
    widgetName: 'widget',
    widgetEventPrefix: '',
    defaultElement: '<div>',

    options: {
      classes: {},
      disabled: false,

      // Callbacks
      create: null
    },

    _createWidget: function (options, element) {
      element = $(element || this.defaultElement || this)[0];
      this.element = $(element);
      this.uuid = widgetUuid++;
      this.eventNamespace = '.' + this.widgetName + this.uuid;

      this.bindings = $();
      this.hoverable = $();
      this.focusable = $();
      this.classesElementLookup = {};

      if (element !== this) {
        $.data(element, this.widgetFullName, this);
        this._on(true, this.element, {
          remove: function (event) {
            if (event.target === element) {
              this.destroy();
            }
          }
        });
        this.document = $(
          element.style
            ? // Element within the document
              element.ownerDocument
            : // Element is window or document
              element.document || element
        );
        this.window = $(
          this.document[0].defaultView || this.document[0].parentWindow
        );
      }

      this.options = $.widget.extend(
        {},
        this.options,
        this._getCreateOptions(),
        options
      );

      this._create();

      if (this.options.disabled) {
        this._setOptionDisabled(this.options.disabled);
      }

      this._trigger('create', null, this._getCreateEventData());
      this._init();
    },

    _getCreateOptions: function () {
      return {};
    },

    _getCreateEventData: $.noop,

    _create: $.noop,

    _init: $.noop,

    destroy: function () {
      var that = this;

      this._destroy();
      $.each(this.classesElementLookup, function (key, value) {
        that._removeClass(value, key);
      });

      // We can probably remove the unbind calls in 2.0
      // all event bindings should go through this._on()
      this.element.off(this.eventNamespace).removeData(this.widgetFullName);
      this.widget().off(this.eventNamespace).removeAttr('aria-disabled');

      // Clean up events and states
      this.bindings.off(this.eventNamespace);
    },

    _destroy: $.noop,

    widget: function () {
      return this.element;
    },

    option: function (key, value) {
      var options = key;
      var parts;
      var curOption;
      var i;

      if (arguments.length === 0) {
        // Don't return a reference to the internal hash
        return $.widget.extend({}, this.options);
      }

      if (typeof key === 'string') {
        // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
        options = {};
        parts = key.split('.');
        key = parts.shift();
        if (parts.length) {
          curOption = options[key] = $.widget.extend({}, this.options[key]);
          for (i = 0; i < parts.length - 1; i++) {
            curOption[parts[i]] = curOption[parts[i]] || {};
            curOption = curOption[parts[i]];
          }
          key = parts.pop();
          if (arguments.length === 1) {
            return curOption[key] === undefined ? null : curOption[key];
          }
          curOption[key] = value;
        } else {
          if (arguments.length === 1) {
            return this.options[key] === undefined ? null : this.options[key];
          }
          options[key] = value;
        }
      }

      this._setOptions(options);

      return this;
    },

    _setOptions: function (options) {
      var key;

      for (key in options) {
        this._setOption(key, options[key]);
      }

      return this;
    },

    _setOption: function (key, value) {
      if (key === 'classes') {
        this._setOptionClasses(value);
      }

      this.options[key] = value;

      if (key === 'disabled') {
        this._setOptionDisabled(value);
      }

      return this;
    },

    _setOptionClasses: function (value) {
      var classKey, elements, currentElements;

      for (classKey in value) {
        currentElements = this.classesElementLookup[classKey];
        if (
          value[classKey] === this.options.classes[classKey] ||
          !currentElements ||
          !currentElements.length
        ) {
          continue;
        }

        // We are doing this to create a new jQuery object because the _removeClass() call
        // on the next line is going to destroy the reference to the current elements being
        // tracked. We need to save a copy of this collection so that we can add the new classes
        // below.
        elements = $(currentElements.get());
        this._removeClass(currentElements, classKey);

        // We don't use _addClass() here, because that uses this.options.classes
        // for generating the string of classes. We want to use the value passed in from
        // _setOption(), this is the new value of the classes option which was passed to
        // _setOption(). We pass this value directly to _classes().
        elements.addClass(
          this._classes({
            element: elements,
            keys: classKey,
            classes: value,
            add: true
          })
        );
      }
    },

    _setOptionDisabled: function (value) {
      this._toggleClass(
        this.widget(),
        this.widgetFullName + '-disabled',
        null,
        !!value
      );

      // If the widget is becoming disabled, then nothing is interactive
      if (value) {
        this._removeClass(this.hoverable, null, 'ui-state-hover');
        this._removeClass(this.focusable, null, 'ui-state-focus');
      }
    },

    enable: function () {
      return this._setOptions({ disabled: false });
    },

    disable: function () {
      return this._setOptions({ disabled: true });
    },

    _classes: function (options) {
      var full = [];
      var that = this;

      options = $.extend(
        {
          element: this.element,
          classes: this.options.classes || {}
        },
        options
      );

      function bindRemoveEvent() {
        options.element.each(function (_, element) {
          var isTracked = $.map(that.classesElementLookup, function (elements) {
            return elements;
          }).some(function (elements) {
            return elements.is(element);
          });

          if (!isTracked) {
            that._on($(element), {
              remove: '_untrackClassesElement'
            });
          }
        });
      }

      function processClassString(classes, checkOption) {
        var current, i;
        for (i = 0; i < classes.length; i++) {
          current = that.classesElementLookup[classes[i]] || $();
          if (options.add) {
            bindRemoveEvent();
            current = $(
              $.uniqueSort(current.get().concat(options.element.get()))
            );
          } else {
            current = $(current.not(options.element).get());
          }
          that.classesElementLookup[classes[i]] = current;
          full.push(classes[i]);
          if (checkOption && options.classes[classes[i]]) {
            full.push(options.classes[classes[i]]);
          }
        }
      }

      if (options.keys) {
        processClassString(options.keys.match(/\S+/g) || [], true);
      }
      if (options.extra) {
        processClassString(options.extra.match(/\S+/g) || []);
      }

      return full.join(' ');
    },

    _untrackClassesElement: function (event) {
      var that = this;
      $.each(that.classesElementLookup, function (key, value) {
        if ($.inArray(event.target, value) !== -1) {
          that.classesElementLookup[key] = $(value.not(event.target).get());
        }
      });

      this._off($(event.target));
    },

    _removeClass: function (element, keys, extra) {
      return this._toggleClass(element, keys, extra, false);
    },

    _addClass: function (element, keys, extra) {
      return this._toggleClass(element, keys, extra, true);
    },

    _toggleClass: function (element, keys, extra, add) {
      add = typeof add === 'boolean' ? add : extra;
      var shift = typeof element === 'string' || element === null,
        options = {
          extra: shift ? keys : extra,
          keys: shift ? element : keys,
          element: shift ? this.element : element,
          add: add
        };
      options.element.toggleClass(this._classes(options), add);
      return this;
    },

    _on: function (suppressDisabledCheck, element, handlers) {
      var delegateElement;
      var instance = this;

      // No suppressDisabledCheck flag, shuffle arguments
      if (typeof suppressDisabledCheck !== 'boolean') {
        handlers = element;
        element = suppressDisabledCheck;
        suppressDisabledCheck = false;
      }

      // No element argument, shuffle and use this.element
      if (!handlers) {
        handlers = element;
        element = this.element;
        delegateElement = this.widget();
      } else {
        element = delegateElement = $(element);
        this.bindings = this.bindings.add(element);
      }

      $.each(handlers, function (event, handler) {
        function handlerProxy() {
          // Allow widgets to customize the disabled handling
          // - disabled as an array instead of boolean
          // - disabled class as method for disabling individual parts
          if (
            !suppressDisabledCheck &&
            (instance.options.disabled === true ||
              $(this).hasClass('ui-state-disabled'))
          ) {
            return;
          }
          return (
            typeof handler === 'string' ? instance[handler] : handler
          ).apply(instance, arguments);
        }

        // Copy the guid so direct unbinding works
        if (typeof handler !== 'string') {
          handlerProxy.guid = handler.guid =
            handler.guid || handlerProxy.guid || $.guid++;
        }

        var match = event.match(/^([\w:-]*)\s*(.*)$/);
        var eventName = match[1] + instance.eventNamespace;
        var selector = match[2];

        if (selector) {
          delegateElement.on(eventName, selector, handlerProxy);
        } else {
          element.on(eventName, handlerProxy);
        }
      });
    },

    _off: function (element, eventName) {
      eventName =
        (eventName || '').split(' ').join(this.eventNamespace + ' ') +
        this.eventNamespace;
      element.off(eventName);

      // Clear the stack to avoid memory leaks (#10056)
      this.bindings = $(this.bindings.not(element).get());
      this.focusable = $(this.focusable.not(element).get());
      this.hoverable = $(this.hoverable.not(element).get());
    },

    _delay: function (handler, delay) {
      var instance = this;
      function handlerProxy() {
        return (
          typeof handler === 'string' ? instance[handler] : handler
        ).apply(instance, arguments);
      }
      return setTimeout(handlerProxy, delay || 0);
    },

    _hoverable: function (element) {
      this.hoverable = this.hoverable.add(element);
      this._on(element, {
        mouseenter: function (event) {
          this._addClass($(event.currentTarget), null, 'ui-state-hover');
        },
        mouseleave: function (event) {
          this._removeClass($(event.currentTarget), null, 'ui-state-hover');
        }
      });
    },

    _focusable: function (element) {
      this.focusable = this.focusable.add(element);
      this._on(element, {
        focusin: function (event) {
          this._addClass($(event.currentTarget), null, 'ui-state-focus');
        },
        focusout: function (event) {
          this._removeClass($(event.currentTarget), null, 'ui-state-focus');
        }
      });
    },

    _trigger: function (type, event, data) {
      var prop, orig;
      var callback = this.options[type];

      data = data || {};
      event = $.Event(event);
      event.type = (
        type === this.widgetEventPrefix ? type : this.widgetEventPrefix + type
      ).toLowerCase();

      // The original event may come from any element
      // so we need to reset the target on the new event
      event.target = this.element[0];

      // Copy original event properties over to the new event
      orig = event.originalEvent;
      if (orig) {
        for (prop in orig) {
          if (!(prop in event)) {
            event[prop] = orig[prop];
          }
        }
      }

      this.element.trigger(event, data);
      return !(
        ($.isFunction(callback) &&
          callback.apply(this.element[0], [event].concat(data)) === false) ||
        event.isDefaultPrevented()
      );
    }
  };

  $.each({ show: 'fadeIn', hide: 'fadeOut' }, function (method, defaultEffect) {
    $.Widget.prototype['_' + method] = function (element, options, callback) {
      if (typeof options === 'string') {
        options = { effect: options };
      }

      var hasOptions;
      var effectName = !options
        ? method
        : options === true || typeof options === 'number'
        ? defaultEffect
        : options.effect || defaultEffect;

      options = options || {};
      if (typeof options === 'number') {
        options = { duration: options };
      }

      hasOptions = !$.isEmptyObject(options);
      options.complete = callback;

      if (options.delay) {
        element.delay(options.delay);
      }

      if (hasOptions && $.effects && $.effects.effect[effectName]) {
        element[method](options);
      } else if (effectName !== method && element[effectName]) {
        element[effectName](options.duration, options.easing, callback);
      } else {
        element.queue(function (next) {
          $(this)[method]();
          if (callback) {
            callback.call(element[0]);
          }
          next();
        });
      }
    };
  });
});


================================================
FILE: package.json
================================================
{
  "name": "blueimp-file-upload",
  "version": "10.32.0",
  "title": "jQuery File Upload",
  "description": "File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery. Supports cross-domain, chunked and resumable file uploads. Works with any server-side platform (Google App Engine, PHP, Python, Ruby on Rails, Java, etc.) that supports standard HTML form file uploads.",
  "keywords": [
    "jquery",
    "file",
    "upload",
    "widget",
    "multiple",
    "selection",
    "drag",
    "drop",
    "progress",
    "preview",
    "cross-domain",
    "cross-site",
    "chunk",
    "resume",
    "gae",
    "go",
    "pyth
Download .txt
gitextract_3s53bgjs/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       └── test.yml
├── .gitignore
├── LICENSE.txt
├── README.md
├── SECURITY.md
├── VULNERABILITIES.md
├── cors/
│   ├── postmessage.html
│   └── result.html
├── css/
│   ├── jquery.fileupload-noscript.css
│   ├── jquery.fileupload-ui-noscript.css
│   ├── jquery.fileupload-ui.css
│   └── jquery.fileupload.css
├── docker-compose.yml
├── index.html
├── js/
│   ├── cors/
│   │   ├── jquery.postmessage-transport.js
│   │   └── jquery.xdr-transport.js
│   ├── demo.js
│   ├── jquery.fileupload-audio.js
│   ├── jquery.fileupload-image.js
│   ├── jquery.fileupload-process.js
│   ├── jquery.fileupload-ui.js
│   ├── jquery.fileupload-validate.js
│   ├── jquery.fileupload-video.js
│   ├── jquery.fileupload.js
│   ├── jquery.iframe-transport.js
│   └── vendor/
│       └── jquery.ui.widget.js
├── package.json
├── server/
│   ├── gae-python/
│   │   ├── app.yaml
│   │   ├── main.py
│   │   └── static/
│   │       └── robots.txt
│   └── php/
│       ├── .dockerignore
│       ├── Dockerfile
│       ├── UploadHandler.php
│       ├── files/
│       │   ├── .gitignore
│       │   └── .htaccess
│       ├── index.php
│       └── php.ini
├── test/
│   ├── index.html
│   ├── unit.js
│   └── vendor/
│       ├── chai.js
│       ├── mocha.css
│       └── mocha.js
└── wdio/
    ├── .eslintrc.js
    ├── .prettierrc.js
    ├── LICENSE.txt
    ├── conf/
    │   ├── chrome.js
    │   └── firefox.js
    ├── hooks/
    │   └── index.js
    ├── test/
    │   ├── pages/
    │   │   └── file-upload.js
    │   └── specs/
    │       └── 01-file-upload.js
    └── wdio.conf.js
Download .txt
SYMBOL INDEX (588 symbols across 9 files)

FILE: js/cors/jquery.xdr-transport.js
  function callback (line 50) | function callback(status, statusText, responses, responseHeaders) {

FILE: js/jquery.fileupload.js
  function getDragHandler (line 65) | function getDragHandler(type) {

FILE: js/vendor/jquery.ui.widget.js
  function _super (line 139) | function _super() {
  function _superApply (line 143) | function _superApply(args) {
  function bindRemoveEvent (line 552) | function bindRemoveEvent() {
  function processClassString (line 568) | function processClassString(classes, checkOption) {
  function handlerProxy (line 652) | function handlerProxy() {
  function handlerProxy (line 700) | function handlerProxy() {

FILE: server/gae-python/main.py
  class CORSHandler (line 35) | class CORSHandler(webapp2.RequestHandler):
    method cors (line 36) | def cors(self):
    method initialize (line 44) | def initialize(self, request, response):
    method json_stringify (line 48) | def json_stringify(self, obj):
    method options (line 51) | def options(self, *args, **kwargs):
  class UploadHandler (line 54) | class UploadHandler(CORSHandler):
    method validate (line 55) | def validate(self, file):
    method validate_redirect (line 66) | def validate_redirect(self, redirect):
    method get_file_size (line 80) | def get_file_size(self, file):
    method write_blob (line 86) | def write_blob(self, data, info):
    method handle_upload (line 113) | def handle_upload(self):
    method head (line 139) | def head(self):
    method get (line 142) | def get(self):
    method post (line 145) | def post(self):
  class FileHandler (line 159) | class FileHandler(CORSHandler):
    method normalize (line 160) | def normalize(self, str):
    method get (line 163) | def get(self, content_type, data_hash, file_name):
    method delete (line 184) | def delete(self, content_type, data_hash, file_name):

FILE: server/php/UploadHandler.php
  class UploadHandler (line 13) | class UploadHandler
    method __construct (line 49) | public function __construct($options = null, $initialize = true, $erro...
    method initialize (line 202) | protected function initialize() {
    method get_full_url (line 224) | protected function get_full_url() {
    method get_user_id (line 237) | protected function get_user_id() {
    method get_user_path (line 242) | protected function get_user_path() {
    method get_upload_path (line 249) | protected function get_upload_path($file_name = null, $version = null) {
    method get_query_separator (line 264) | protected function get_query_separator($url) {
    method get_download_url (line 268) | protected function get_download_url($file_name, $version = null, $dire...
    method set_additional_file_properties (line 292) | protected function set_additional_file_properties($file) {
    method fix_integer_overflow (line 308) | protected function fix_integer_overflow($size) {
    method get_file_size (line 315) | protected function get_file_size($file_path, $clear_stat_cache = false) {
    method is_valid_file_object (line 326) | protected function is_valid_file_object($file_name) {
    method get_file_object (line 334) | protected function get_file_object($file_name) {
    method get_file_objects (line 358) | protected function get_file_objects($iteration_method = 'get_file_obje...
    method count_file_objects (line 369) | protected function count_file_objects() {
    method get_error_message (line 373) | protected function get_error_message($error) {
    method get_config_bytes (line 378) | public function get_config_bytes($val) {
    method validate_image_file (line 397) | protected function validate_image_file($uploaded_file, $file, $error, ...
    method validate (line 443) | protected function validate($uploaded_file, $file, $error, $index, $co...
    method upcount_name_callback (line 490) | protected function upcount_name_callback($matches) {
    method upcount_name (line 496) | protected function upcount_name($name) {
    method get_unique_filename (line 505) | protected function get_unique_filename($file_path, $name, $size, $type...
    method get_valid_image_extensions (line 522) | protected function get_valid_image_extensions($file_path) {
    method fix_file_extension (line 533) | protected function fix_file_extension($file_path, $name, $size, $type,...
    method trim_file_name (line 556) | protected function trim_file_name($file_path, $name, $size, $type, $er...
    method get_file_name (line 579) | protected function get_file_name($file_path, $name, $size, $type, $error,
    method get_scaled_image_file_paths (line 595) | protected function get_scaled_image_file_paths($file_name, $version) {
    method gd_get_image_object (line 609) | protected function gd_get_image_object($file_path, $func, $no_cache = ...
    method gd_set_image_object (line 617) | protected function gd_set_image_object($file_path, $image) {
    method gd_destroy_image_object (line 622) | protected function gd_destroy_image_object($file_path) {
    method gd_imageflip (line 627) | protected function gd_imageflip($image, $mode) {
    method gd_orient_image (line 669) | protected function gd_orient_image($file_path, $src_img) {
    method gd_create_scaled_image (line 726) | protected function gd_create_scaled_image($file_name, $version, $optio...
    method imagick_get_image_object (line 838) | protected function imagick_get_image_object($file_path, $no_cache = fa...
    method imagick_set_image_object (line 858) | protected function imagick_set_image_object($file_path, $image) {
    method imagick_destroy_image_object (line 863) | protected function imagick_destroy_image_object($file_path) {
    method imagick_orient_image (line 868) | protected function imagick_orient_image($image) {
    method imagick_create_scaled_image (line 902) | protected function imagick_create_scaled_image($file_name, $version, $...
    method imagemagick_create_scaled_image (line 989) | protected function imagemagick_create_scaled_image($file_name, $versio...
    method get_image_size (line 1033) | protected function get_image_size($file_path) {
    method create_scaled_image (line 1068) | protected function create_scaled_image($file_name, $version, $options) {
    method destroy_image_object (line 1083) | protected function destroy_image_object($file_path) {
    method imagetype (line 1089) | protected function imagetype($file_path) {
    method is_valid_image_file (line 1108) | protected function is_valid_image_file($file_path) {
    method has_image_file_extension (line 1112) | protected function has_image_file_extension($file_path) {
    method handle_image_file (line 1116) | protected function handle_image_file($file_path, $file) {
    method handle_file_upload (line 1140) | protected function handle_file_upload($uploaded_file, $name, $size, $t...
    method readfile (line 1197) | protected function readfile($file_path) {
    method body (line 1213) | protected function body($str) {
    method header (line 1217) | protected function header($str) {
    method get_upload_data (line 1221) | protected function get_upload_data($id) {
    method get_post_param (line 1225) | protected function get_post_param($id) {
    method get_query_param (line 1229) | protected function get_query_param($id) {
    method get_server_var (line 1233) | protected function get_server_var($id) {
    method handle_form_data (line 1237) | protected function handle_form_data($file, $index) {
    method get_version_param (line 1241) | protected function get_version_param() {
    method get_singular_param_name (line 1245) | protected function get_singular_param_name() {
    method get_file_name_param (line 1249) | protected function get_file_name_param() {
    method get_file_names_params (line 1254) | protected function get_file_names_params() {
    method get_file_type (line 1265) | protected function get_file_type($file_path) {
    method download (line 1279) | protected function download() {
    method send_content_type_header (line 1321) | protected function send_content_type_header() {
    method send_access_control_headers (line 1330) | protected function send_access_control_headers() {
    method generate_response (line 1340) | public function generate_response($content, $print_response = true) {
    method get_response (line 1363) | public function get_response () {
    method head (line 1367) | public function head() {
    method get (line 1379) | public function get($print_response = true) {
    method post (line 1396) | public function post($print_response = true) {
    method delete (line 1452) | public function delete($print_response = true) {
    method basename (line 1476) | protected function basename($filepath, $suffix = null) {

FILE: test/unit.js
  function createFileuploadForm (line 57) | function createFileuploadForm() {
  function deleteFiles (line 74) | function deleteFiles(files, callback) {

FILE: test/vendor/chai.js
  function r (line 1) | function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==...
  function Assertion (line 160) | function Assertion (obj, msg, ssfi, lockSsfi) {
  function an (line 661) | function an (type, msg) {
  function SameValueZero (line 824) | function SameValueZero(a, b) {
  function includeChainingBehavior (line 828) | function includeChainingBehavior () {
  function include (line 832) | function include (val, msg) {
  function checkArguments (line 1320) | function checkArguments () {
  function assertEqual (line 1378) | function assertEqual (val, msg) {
  function assertEql (line 1443) | function assertEql(obj, msg) {
  function assertAbove (line 1501) | function assertAbove (n, msg) {
  function assertLeast (line 1604) | function assertLeast (n, msg) {
  function assertBelow (line 1707) | function assertBelow (n, msg) {
  function assertMost (line 1810) | function assertMost (n, msg) {
  function assertInstanceOf (line 2010) | function assertInstanceOf (constructor, msg) {
  function assertProperty (line 2159) | function assertProperty (name, val, msg) {
  function assertOwnProperty (line 2248) | function assertOwnProperty (name, value, msg) {
  function assertOwnPropertyDescriptor (line 2375) | function assertOwnPropertyDescriptor (name, descriptor, msg) {
  function assertLengthChain (line 2463) | function assertLengthChain () {
  function assertLength (line 2467) | function assertLength (n, msg) {
  function assertMatch (line 2526) | function assertMatch(re, msg) {
  function assertKeys (line 2682) | function assertKeys (keys) {
  function assertThrows (line 2976) | function assertThrows (errorLike, errMsgMatcher, msg) {
  function respondTo (line 3171) | function respondTo (method, msg) {
  function satisfy (line 3251) | function satisfy (matcher, msg) {
  function closeTo (line 3305) | function closeTo(expected, delta, msg) {
  function isSubsetOf (line 3332) | function isSubsetOf(subset, superset, cmp, contains, ordered) {
  function oneOf (line 3495) | function oneOf (list, msg) {
  function assertChanges (line 3608) | function assertChanges (subject, prop, msg) {
  function assertIncreases (line 3725) | function assertIncreases (subject, prop, msg) {
  function assertDecreases (line 3844) | function assertDecreases (subject, prop, msg) {
  function assertDelta (line 3950) | function assertDelta(delta, msg) {
  function loadShould (line 7341) | function loadShould () {
  function addProperty (line 8217) | function addProperty(property) {
  function inspect (line 8432) | function inspect(obj, showHidden, depth, colors) {
  function formatValue (line 8454) | function formatValue(ctx, value, recurseTimes) {
  function formatPrimitive (line 8605) | function formatPrimitive(ctx, value) {
  function formatError (line 8634) | function formatError(value) {
  function formatArray (line 8638) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  function formatTypedArray (line 8658) | function formatTypedArray(value) {
  function formatProperty (line 8678) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
  function reduceToSingleString (line 8740) | function reduceToSingleString(output, base, braces) {
  function isTypedArray (line 8757) | function isTypedArray(ar) {
  function isArray (line 8763) | function isArray(ar) {
  function isRegExp (line 8768) | function isRegExp(re) {
  function isDate (line 8772) | function isDate(d) {
  function isError (line 8776) | function isError(e) {
  function objectToString (line 8780) | function objectToString(o) {
  function isNaN (line 8803) | function isNaN(value) {
  function stringDistanceCapped (line 9262) | function stringDistanceCapped(strA, strB, cap) {
  function exclude (line 9391) | function exclude () {
  function AssertionError (line 9430) | function AssertionError (message, _props, ssf) {
  function compatibleInstance (line 9524) | function compatibleInstance(thrown, errorLike) {
  function compatibleConstructor (line 9544) | function compatibleConstructor(thrown, errorLike) {
  function compatibleMessage (line 9570) | function compatibleMessage(thrown, errMatcher) {
  function getFunctionName (line 9594) | function getFunctionName(constructorFn) {
  function getConstructorName (line 9620) | function getConstructorName(errorLike) {
  function getMessage (line 9648) | function getMessage(errorLike) {
  function FakeMap (line 9677) | function FakeMap() {
  function memoizeCompare (line 9704) | function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) {
  function memoizeSet (line 9727) | function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, resul...
  function deepEqual (line 9761) | function deepEqual(leftHandOperand, rightHandOperand, options) {
  function simpleEqual (line 9782) | function simpleEqual(leftHandOperand, rightHandOperand) {
  function extensiveDeepEqual (line 9818) | function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) {
  function extensiveDeepEqualByType (line 9864) | function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, lef...
  function regexpEqual (line 9916) | function regexpEqual(leftHandOperand, rightHandOperand) {
  function entriesEqual (line 9929) | function entriesEqual(leftHandOperand, rightHandOperand, options) {
  function iterableEqual (line 9957) | function iterableEqual(leftHandOperand, rightHandOperand, options) {
  function generatorEqual (line 9983) | function generatorEqual(leftHandOperand, rightHandOperand, options) {
  function hasIteratorFunction (line 9993) | function hasIteratorFunction(target) {
  function getIteratorEntries (line 10007) | function getIteratorEntries(target) {
  function getGeneratorEntries (line 10024) | function getGeneratorEntries(generator) {
  function getEnumerableKeys (line 10040) | function getEnumerableKeys(target) {
  function keysEqual (line 10058) | function keysEqual(leftHandOperand, rightHandOperand, keys, options) {
  function objectEqual (line 10081) | function objectEqual(leftHandOperand, rightHandOperand, options) {
  function isPrimitive (line 10120) | function isPrimitive(value) {
  function getFuncName (line 10148) | function getFuncName(aFunc) {
  function hasProperty (line 10217) | function hasProperty(obj, name) {
  function parsePath (line 10245) | function parsePath(path) {
  function internalGetPathValue (line 10277) | function internalGetPathValue(obj, parsed, pathDepth) {
  function internalSetPathValue (line 10314) | function internalSetPathValue(obj, val, parsed) {
  function getPathInfo (line 10368) | function getPathInfo(obj, path) {
  function getPathValue (line 10412) | function getPathValue(obj, path) {
  function setPathValue (line 10450) | function setPathValue(obj, path, val) {
  function typeDetect (line 10508) | function typeDetect(obj) {

FILE: test/vendor/mocha.js
  function r (line 1) | function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==...
  function timeslice (line 79) | function timeslice() {
  function isPermitted (line 267) | function isPermitted() {
  function canNotify (line 303) | function canNotify(value) {
  function display (line 318) | function display(runner) {
  function notPermitted (line 363) | function notPermitted(err) {
  function Progress (line 380) | function Progress() {
  function Context (line 522) | function Context() {}
  function createNoFilesMatchPatternError (line 626) | function createNoFilesMatchPatternError(message, pattern) {
  function createInvalidReporterError (line 641) | function createInvalidReporterError(message, reporter) {
  function createInvalidInterfaceError (line 656) | function createInvalidInterfaceError(message, ui) {
  function createUnsupportedError (line 670) | function createUnsupportedError(message) {
  function createMissingArgumentError (line 685) | function createMissingArgumentError(message, argument, expected) {
  function createInvalidArgumentTypeError (line 698) | function createInvalidArgumentTypeError(message, argument, expected) {
  function createInvalidArgumentValueError (line 717) | function createInvalidArgumentValueError(message, argument, value, reaso...
  function createInvalidExceptionError (line 733) | function createInvalidExceptionError(message, value) {
  function Hook (line 772) | function Hook(title, fn) {
  function shouldBeTested (line 943) | function shouldBeTested(suite) {
  function visit (line 1140) | function visit(obj, file) {
  function Mocha (line 1488) | function Mocha(options) {
  function done (line 2323) | function done(failures) {
  function Pending (line 2359) | function Pending(message) {
  function stringifyDiffObjs (line 2530) | function stringifyDiffObjs(err) {
  function Base (line 2661) | function Base(runner, options) {
  function pad (line 2743) | function pad(str, len) {
  function inlineDiff (line 2756) | function inlineDiff(actual, expected) {
  function unifiedDiff (line 2793) | function unifiedDiff(actual, expected) {
  function errorDiff (line 2836) | function errorDiff(actual, expected) {
  function colorLines (line 2859) | function colorLines(name, str) {
  function sameType (line 2881) | function sameType(a, b) {
  function Doc (line 2923) | function Doc(runner, options) {
  function Dot (line 3016) | function Dot(runner, options) {
  function HTML (line 3126) | function HTML(runner, options) {
  function makeUrl (line 3322) | function makeUrl(s) {
  function error (line 3379) | function error(msg) {
  function fragment (line 3388) | function fragment(html) {
  function hideSuitesWithout (line 3412) | function hideSuitesWithout(classname) {
  function unhide (line 3425) | function unhide() {
  function text (line 3438) | function text(el, contents) {
  function on (line 3449) | function on(el, event, fn) {
  function JSONStream (line 3514) | function JSONStream(runner, options) {
  function writeEvent (line 3551) | function writeEvent(event) {
  function clean (line 3563) | function clean(test) {
  function JSONReporter (line 3609) | function JSONReporter(runner, options) {
  function clean (line 3657) | function clean(test) {
  function cleanCycles (line 3679) | function cleanCycles(obj) {
  function errorJSON (line 3703) | function errorJSON(err) {
  function Landing (line 3769) | function Landing(runner, options) {
  function List (line 3864) | function List(runner, options) {
  function Markdown (line 3948) | function Markdown(runner, options) {
  function Min (line 4059) | function Min(runner, options) {
  function NyanCat (line 4115) | function NyanCat(runner, options) {
  function draw (line 4185) | function draw(type, n) {
  function write (line 4353) | function write(string) {
  function Progress (line 4401) | function Progress(runner, options) {
  function Spec (line 4505) | function Spec(runner, options) {
  function TAP (line 4607) | function TAP(runner, options) {
  function title (line 4661) | function title(test) {
  function println (line 4672) | function println(format, varArgs) {
  function createProducer (line 4686) | function createProducer(tapVersion) {
  function TAPProducer (line 4712) | function TAPProducer() {}
  function TAP12Producer (line 4791) | function TAP12Producer() {
  function TAP13Producer (line 4824) | function TAP13Producer() {
  function XUnit (line 4914) | function XUnit(runner, options) {
  function tag (line 5066) | function tag(name, attrs, close, content) {
  function Runnable (line 5118) | function Runnable(title, fn) {
  function multiple (line 5392) | function multiple(err) {
  function done (line 5407) | function done(err) {
  function callFn (line 5482) | function callFn(fn) {
  function callFnAsync (line 5510) | function callFnAsync(fn) {
  function Runner (line 5724) | function Runner(suite, delay) {
  function next (line 5960) | function next(i) {
  function next (line 6046) | function next(suite) {
  function hookErr (line 6160) | function hookErr(_, errSuite, after) {
  function next (line 6186) | function next(err, errSuite) {
  function alwaysFalse (line 6313) | function alwaysFalse() {
  function next (line 6337) | function next(errSuite) {
  function done (line 6371) | function done(errSuite) {
  function uncaught (line 6492) | function uncaught(err) {
  function start (line 6496) | function start() {
  function filterLeaks (line 6568) | function filterLeaks(ok, globals) {
  function isError (line 6611) | function isError(err) {
  function thrown2Error (line 6623) | function thrown2Error(err) {
  function createStatsCollector (line 6680) | function createStatsCollector(runner) {
  function Suite (line 6773) | function Suite(title, parentContext, isRoot) {
  function cleanArrReferences (line 7250) | function cleanArrReferences(arr) {
  function Test (line 7388) | function Test(title, fn) {
  function highlight (line 7564) | function highlight(js) {
  function emptyRepresentation (line 7609) | function emptyRepresentation(value, typeHint) {
  function jsonStringify (line 7719) | function jsonStringify(object, spaces, depth) {
  function withStack (line 7821) | function withStack(value, fn) {
  function hasMatchingExtname (line 7892) | function hasMatchingExtname(pathname, exts) {
  function isHiddenOnUnix (line 7914) | function isHiddenOnUnix(pathname) {
  function emitWarning (line 8022) | function emitWarning(msg, type) {
  function isMochaInternal (line 8085) | function isMochaInternal(line) {
  function isNodeInternal (line 8093) | function isNodeInternal(line) {
  function getLens (line 8316) | function getLens (b64) {
  function byteLength (line 8336) | function byteLength (b64) {
  function _byteLength (line 8343) | function _byteLength (b64, validLen, placeHoldersLen) {
  function toByteArray (line 8347) | function toByteArray (b64) {
  function tripletToBase64 (line 8392) | function tripletToBase64 (num) {
  function encodeChunk (line 8399) | function encodeChunk (uint8, start, end) {
  function fromByteArray (line 8412) | function fromByteArray (uint8) {
  function BrowserStdout (line 8459) | function BrowserStdout(opts) {
  function typedArraySupport (line 8526) | function typedArraySupport () {
  function createBuffer (line 8553) | function createBuffer (length) {
  function Buffer (line 8573) | function Buffer (arg, encodingOrOffset, length) {
  function from (line 8599) | function from (value, encodingOrOffset, length) {
  function assertSize (line 8664) | function assertSize (size) {
  function alloc (line 8672) | function alloc (size, fill, encoding) {
  function allocUnsafe (line 8696) | function allocUnsafe (size) {
  function fromString (line 8714) | function fromString (string, encoding) {
  function fromArrayLike (line 8738) | function fromArrayLike (array) {
  function fromArrayBuffer (line 8747) | function fromArrayBuffer (array, byteOffset, length) {
  function fromObject (line 8770) | function fromObject (obj) {
  function checked (line 8795) | function checked (length) {
  function SlowBuffer (line 8805) | function SlowBuffer (length) {
  function byteLength (line 8896) | function byteLength (string, encoding) {
  function slowToString (line 8945) | function slowToString (encoding, start, end) {
  function swap (line 9023) | function swap (b, n, m) {
  function bidirectionalIndexOf (line 9163) | function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
  function arrayIndexOf (line 9219) | function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
  function hexWrite (line 9287) | function hexWrite (buf, string, offset, length) {
  function utf8Write (line 9312) | function utf8Write (buf, string, offset, length) {
  function asciiWrite (line 9316) | function asciiWrite (buf, string, offset, length) {
  function latin1Write (line 9320) | function latin1Write (buf, string, offset, length) {
  function base64Write (line 9324) | function base64Write (buf, string, offset, length) {
  function ucs2Write (line 9328) | function ucs2Write (buf, string, offset, length) {
  function base64Slice (line 9410) | function base64Slice (buf, start, end) {
  function utf8Slice (line 9418) | function utf8Slice (buf, start, end) {
  function decodeCodePointsArray (line 9496) | function decodeCodePointsArray (codePoints) {
  function asciiSlice (line 9514) | function asciiSlice (buf, start, end) {
  function latin1Slice (line 9524) | function latin1Slice (buf, start, end) {
  function hexSlice (line 9534) | function hexSlice (buf, start, end) {
  function utf16leSlice (line 9547) | function utf16leSlice (buf, start, end) {
  function checkOffset (line 9586) | function checkOffset (offset, ext, length) {
  function checkInt (line 9761) | function checkInt (buf, value, offset, ext, max, min) {
  function checkIEEE754 (line 9949) | function checkIEEE754 (buf, value, offset, ext, max, min) {
  function writeFloat (line 9954) | function writeFloat (buf, value, offset, littleEndian, noAssert) {
  function writeDouble (line 9972) | function writeDouble (buf, value, offset, littleEndian, noAssert) {
  function base64clean (line 10111) | function base64clean (str) {
  function toHex (line 10125) | function toHex (n) {
  function utf8ToBytes (line 10130) | function utf8ToBytes (string, units) {
  function asciiToBytes (line 10210) | function asciiToBytes (str) {
  function utf16leToBytes (line 10219) | function utf16leToBytes (str, units) {
  function base64ToBytes (line 10235) | function base64ToBytes (str) {
  function blitBuffer (line 10239) | function blitBuffer (src, dst, offset, length) {
  function isInstance (line 10250) | function isInstance (obj, type) {
  function numberIsNaN (line 10255) | function numberIsNaN (obj) {
  function isArray (line 10287) | function isArray(arg) {
  function isBoolean (line 10295) | function isBoolean(arg) {
  function isNull (line 10300) | function isNull(arg) {
  function isNullOrUndefined (line 10305) | function isNullOrUndefined(arg) {
  function isNumber (line 10310) | function isNumber(arg) {
  function isString (line 10315) | function isString(arg) {
  function isSymbol (line 10320) | function isSymbol(arg) {
  function isUndefined (line 10325) | function isUndefined(arg) {
  function isRegExp (line 10330) | function isRegExp(re) {
  function isObject (line 10335) | function isObject(arg) {
  function isDate (line 10340) | function isDate(d) {
  function isError (line 10345) | function isError(e) {
  function isFunction (line 10350) | function isFunction(arg) {
  function isPrimitive (line 10355) | function isPrimitive(arg) {
  function objectToString (line 10367) | function objectToString(o) {
  function _typeof (line 10376) | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbo...
  function useColors (line 10403) | function useColors() {
  function formatArgs (line 10431) | function formatArgs(args) {
  function log (line 10468) | function log() {
  function save (line 10483) | function save(namespaces) {
  function load (line 10502) | function load() {
  function localstorage (line 10530) | function localstorage() {
  function setup (line 10563) | function setup(env) {
  function __webpack_require__ (line 10920) | function __webpack_require__(moduleId) {
  function _interopRequireDefault (line 10996) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function Diff (line 11045) | function Diff() {}
  function done (line 11060) | function done(value) {
  function execEditLength (line 11092) | function execEditLength() {
  function buildValues (line 11219) | function buildValues(diff, components, newString, oldString, useLongestT...
  function clonePath (line 11272) | function clonePath(path) {
  function _interopRequireDefault (line 11292) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffChars (line 11295) | function diffChars(oldStr, newStr, options) {
  function _interopRequireDefault (line 11318) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffWords (line 11366) | function diffWords(oldStr, newStr, options) {
  function diffWordsWithSpace (line 11371) | function diffWordsWithSpace(oldStr, newStr, options) {
  function generateOptions (line 11385) | function generateOptions(options, defaults) {
  function _interopRequireDefault (line 11418) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffLines (line 11447) | function diffLines(oldStr, newStr, callback) {
  function diffTrimmedLines (line 11450) | function diffTrimmedLines(oldStr, newStr, callback) {
  function _interopRequireDefault (line 11471) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffSentences (line 11478) | function diffSentences(oldStr, newStr, callback) {
  function _interopRequireDefault (line 11498) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffCss (line 11505) | function diffCss(oldStr, newStr, callback) {
  function _interopRequireDefault (line 11531) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffJson (line 11558) | function diffJson(oldObj, newObj, options) {
  function canonicalize (line 11564) | function canonicalize(obj, stack, replacementStack, replacer, key) {
  function _interopRequireDefault (line 11639) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function diffArrays (line 11649) | function diffArrays(oldArr, newArr, callback) {
  function _interopRequireDefault (line 11671) | function _interopRequireDefault(obj) { return obj && obj.__esModule ? ob...
  function applyPatch (line 11673) | function applyPatch(source, uniDiff) {
  function applyPatches (line 11809) | function applyPatches(uniDiff, options) {
  function parsePatch (line 11849) | function parsePatch(uniDiff) {
  function _toConsumableArray (line 12063) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...
  function calcLineCount (line 12065) | function calcLineCount(hunk) {
  function merge (line 12083) | function merge(mine, theirs, base) {
  function loadPatch (line 12159) | function loadPatch(param, base) {
  function fileNameChanged (line 12176) | function fileNameChanged(patch) {
  function selectField (line 12180) | function selectField(index, mine, theirs) {
  function hunkBefore (line 12189) | function hunkBefore(test, check) {
  function cloneHunk (line 12193) | function cloneHunk(hunk, offset) {
  function mergeLines (line 12201) | function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) {
  function mutualChange (line 12253) | function mutualChange(hunk, mine, their) {
  function removal (line 12280) | function removal(hunk, mine, their, swap) {
  function conflict (line 12292) | function conflict(hunk, mine, their) {
  function insertLeading (line 12301) | function insertLeading(hunk, insert, their) {
  function insertTrailing (line 12308) | function insertTrailing(hunk, insert) {
  function collectChange (line 12315) | function collectChange(state) {
  function collectContext (line 12336) | function collectContext(state, matchChanges) {
  function allRemoves (line 12393) | function allRemoves(changes) {
  function skipRemoveSuperset (line 12398) | function skipRemoveSuperset(state, removeChanges, delta) {
  function calcOldNewLineCount (line 12410) | function calcOldNewLineCount(lines) {
  function _toConsumableArray (line 12462) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...
  function structuredPatch (line 12464) | function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHe...
  function createTwoFilesPatch (line 12577) | function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, o...
  function createPatch (line 12597) | function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, opt...
  function arrayEqual (line 12612) | function arrayEqual(a, b) {
  function arrayStartsWith (line 12620) | function arrayStartsWith(array, start) {
  function convertChangesToDMP (line 12645) | function convertChangesToDMP(changes) {
  function convertChangesToXML (line 12674) | function convertChangesToXML(changes) {
  function escapeHTML (line 12695) | function escapeHTML(s) {
  function EventEmitter (line 12750) | function EventEmitter() {
  function $getMaxListeners (line 12803) | function $getMaxListeners(that) {
  function emitNone (line 12818) | function emitNone(handler, isFn, self) {
  function emitOne (line 12828) | function emitOne(handler, isFn, self, arg1) {
  function emitTwo (line 12838) | function emitTwo(handler, isFn, self, arg1, arg2) {
  function emitThree (line 12848) | function emitThree(handler, isFn, self, arg1, arg2, arg3) {
  function emitMany (line 12859) | function emitMany(handler, isFn, self, args) {
  function _addListener (line 12927) | function _addListener(target, type, listener, prepend) {
  function onceWrapper (line 13005) | function onceWrapper() {
  function _onceWrap (line 13028) | function _onceWrap(target, type, listener) {
  function _listeners (line 13155) | function _listeners(target, type, unwrap) {
  function listenerCount (line 13188) | function listenerCount(type) {
  function spliceOne (line 13209) | function spliceOne(list, index) {
  function arrayClone (line 13215) | function arrayClone(arr, n) {
  function unwrapListeners (line 13222) | function unwrapListeners(arr) {
  function objectCreatePolyfill (line 13230) | function objectCreatePolyfill(proto) {
  function objectKeysPolyfill (line 13235) | function objectKeysPolyfill(obj) {
  function functionBindPolyfill (line 13242) | function functionBindPolyfill(context) {
  function isBuffer (line 13826) | function isBuffer (obj) {
  function isSlowBuffer (line 13831) | function isSlowBuffer (obj) {
  function mkdirP (line 13850) | function mkdirP (p, opts, f, made) {
  function parse (line 13992) | function parse(str) {
  function fmtShort (line 14057) | function fmtShort(ms) {
  function fmtLong (line 14082) | function fmtLong(ms) {
  function plural (line 14103) | function plural(ms, msAbs, n, name) {
  function nextTick (line 14426) | function nextTick(fn, arg1, arg2, arg3) {
  function defaultSetTimout (line 14474) | function defaultSetTimout() {
  function defaultClearTimeout (line 14477) | function defaultClearTimeout () {
  function runTimeout (line 14500) | function runTimeout(fun) {
  function runClearTimeout (line 14525) | function runClearTimeout(marker) {
  function cleanUpNextTick (line 14557) | function cleanUpNextTick() {
  function drainQueue (line 14572) | function drainQueue() {
  function Item (line 14610) | function Item(fun, array) {
  function noop (line 14624) | function noop() {}
  function Duplex (line 14715) | function Duplex(options) {
  function onend (line 14742) | function onend() {
  function onEndNT (line 14752) | function onEndNT(self) {
  function PassThrough (line 14822) | function PassThrough(options) {
  function _uint8ArrayToBuffer (line 14889) | function _uint8ArrayToBuffer(chunk) {
  function _isUint8Array (line 14892) | function _isUint8Array(obj) {
  function prependListener (line 14921) | function prependListener(emitter, event, fn) {
  function ReadableState (line 14933) | function ReadableState(options, stream) {
  function Readable (line 15010) | function Readable(options) {
  function readableAddChunk (line 15085) | function readableAddChunk(stream, chunk, encoding, addToFront, skipChunk...
  function addChunk (line 15121) | function addChunk(stream, state, chunk, addToFront) {
  function chunkInvalid (line 15135) | function chunkInvalid(state, chunk) {
  function needMoreData (line 15150) | function needMoreData(state) {
  function computeNewHighWaterMark (line 15168) | function computeNewHighWaterMark(n) {
  function howMuchToRead (line 15187) | function howMuchToRead(n, state) {
  function onEofChunk (line 15306) | function onEofChunk(stream, state) {
  function emitReadable (line 15324) | function emitReadable(stream) {
  function emitReadable_ (line 15334) | function emitReadable_(stream) {
  function maybeReadMore (line 15346) | function maybeReadMore(stream, state) {
  function maybeReadMore_ (line 15353) | function maybeReadMore_(stream, state) {
  function onunpipe (line 15397) | function onunpipe(readable, unpipeInfo) {
  function onend (line 15407) | function onend() {
  function cleanup (line 15420) | function cleanup() {
  function ondata (line 15448) | function ondata(chunk) {
  function onerror (line 15468) | function onerror(er) {
  function onclose (line 15479) | function onclose() {
  function onfinish (line 15484) | function onfinish() {
  function unpipe (line 15491) | function unpipe() {
  function pipeOnDrain (line 15508) | function pipeOnDrain(src) {
  function nReadingNextTick (line 15595) | function nReadingNextTick(self) {
  function resume (line 15612) | function resume(stream, state) {
  function resume_ (line 15619) | function resume_(stream, state) {
  function flow (line 15642) | function flow(stream) {
  function fromList (line 15728) | function fromList(n, state) {
  function fromListPartial (line 15748) | function fromListPartial(n, list, hasStrings) {
  function copyFromBufferString (line 15768) | function copyFromBufferString(n, list) {
  function copyFromBuffer (line 15797) | function copyFromBuffer(n, list) {
  function endReadable (line 15824) | function endReadable(stream) {
  function endReadableNT (line 15837) | function endReadableNT(state, stream) {
  function indexOf (line 15846) | function indexOf(xs, x) {
  function afterTransform (line 15930) | function afterTransform(er, data) {
  function Transform (line 15955) | function Transform(options) {
  function prefinish (line 15987) | function prefinish() {
  function done (line 16054) | function done(stream, er, data) {
  function WriteReq (line 16105) | function WriteReq(chunk, encoding, cb) {
  function CorkedRequest (line 16114) | function CorkedRequest(state) {
  function _uint8ArrayToBuffer (line 16154) | function _uint8ArrayToBuffer(chunk) {
  function _isUint8Array (line 16157) | function _isUint8Array(obj) {
  function nop (line 16167) | function nop() {}
  function WritableState (line 16169) | function WritableState(options, stream) {
  function Writable (line 16319) | function Writable(options) {
  function writeAfterEnd (line 16356) | function writeAfterEnd(stream, cb) {
  function validChunk (line 16366) | function validChunk(stream, state, chunk, cb) {
  function decodeChunk (line 16433) | function decodeChunk(state, chunk, encoding) {
  function writeOrBuffer (line 16453) | function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {
  function doWrite (line 16492) | function doWrite(stream, state, writev, len, chunk, encoding, cb) {
  function onwriteError (line 16501) | function onwriteError(stream, state, sync, er, cb) {
  function onwriteStateUpdate (line 16525) | function onwriteStateUpdate(state) {
  function onwrite (line 16532) | function onwrite(stream, er) {
  function afterWrite (line 16557) | function afterWrite(stream, state, finished, cb) {
  function onwriteDrain (line 16567) | function onwriteDrain(stream, state) {
  function clearBuffer (line 16575) | function clearBuffer(stream, state) {
  function needFinish (line 16666) | function needFinish(state) {
  function callFinal (line 16669) | function callFinal(stream, state) {
  function prefinish (line 16680) | function prefinish(stream, state) {
  function finishMaybe (line 16693) | function finishMaybe(stream, state) {
  function endWritable (line 16705) | function endWritable(stream, state, cb) {
  function onCorkedFinish (line 16715) | function onCorkedFinish(corkReq, state, err) {
  function _classCallCheck (line 16761) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function copyBuffer (line 16766) | function copyBuffer(src, target, offset) {
  function BufferList (line 16771) | function BufferList() {
  function destroy (line 16847) | function destroy(err, cb) {
  function undestroy (line 16888) | function undestroy() {
  function emitErrorNT (line 16905) | function emitErrorNT(self, err) {
  function copyProps (line 16940) | function copyProps (src, dst) {
  function SafeBuffer (line 16953) | function SafeBuffer (arg, encodingOrOffset, length) {
  function Stream (line 17040) | function Stream() {
  function ondata (line 17047) | function ondata(chunk) {
  function ondrain (line 17057) | function ondrain() {
  function onend (line 17073) | function onend() {
  function onclose (line 17081) | function onclose() {
  function onerror (line 17089) | function onerror(er) {
  function cleanup (line 17100) | function cleanup() {
  function _normalizeEncoding (line 17166) | function _normalizeEncoding(enc) {
  function normalizeEncoding (line 17196) | function normalizeEncoding(enc) {
  function StringDecoder (line 17206) | function StringDecoder(encoding) {
  function utf8CheckByte (line 17267) | function utf8CheckByte(byte) {
  function utf8CheckIncomplete (line 17275) | function utf8CheckIncomplete(self, buf, i) {
  function utf8CheckExtraBytes (line 17308) | function utf8CheckExtraBytes(self, buf, p) {
  function utf8FillLast (line 17328) | function utf8FillLast(buf) {
  function utf8Text (line 17343) | function utf8Text(buf, i) {
  function utf8End (line 17354) | function utf8End(buf) {
  function utf16Text (line 17364) | function utf16Text(buf, i) {
  function utf16End (line 17387) | function utf16End(buf) {
  function base64Text (line 17396) | function base64Text(buf, i) {
  function base64End (line 17410) | function base64End(buf) {
  function simpleWrite (line 17417) | function simpleWrite(buf) {
  function simpleEnd (line 17421) | function simpleEnd(buf) {
  function Timeout (line 17443) | function Timeout(id, clearFn) {
  function deprecate (line 17530) | function deprecate (fn, msg) {
  function config (line 17561) | function config (name) {
  function deprecated (line 17660) | function deprecated() {
  function inspect (line 17707) | function inspect(obj, opts) {
  function stylizeWithColor (line 17765) | function stylizeWithColor(str, styleType) {
  function stylizeNoColor (line 17777) | function stylizeNoColor(str, styleType) {
  function arrayToHash (line 17782) | function arrayToHash(array) {
  function formatValue (line 17793) | function formatValue(ctx, value, recurseTimes) {
  function formatPrimitive (line 17906) | function formatPrimitive(ctx, value) {
  function formatError (line 17925) | function formatError(value) {
  function formatArray (line 17930) | function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
  function formatProperty (line 17950) | function formatProperty(ctx, value, recurseTimes, visibleKeys, key, arra...
  function reduceToSingleString (line 18009) | function reduceToSingleString(output, base, braces) {
  function isArray (line 18032) | function isArray(ar) {
  function isBoolean (line 18037) | function isBoolean(arg) {
  function isNull (line 18042) | function isNull(arg) {
  function isNullOrUndefined (line 18047) | function isNullOrUndefined(arg) {
  function isNumber (line 18052) | function isNumber(arg) {
  function isString (line 18057) | function isString(arg) {
  function isSymbol (line 18062) | function isSymbol(arg) {
  function isUndefined (line 18067) | function isUndefined(arg) {
  function isRegExp (line 18072) | function isRegExp(re) {
  function isObject (line 18077) | function isObject(arg) {
  function isDate (line 18082) | function isDate(d) {
  function isError (line 18087) | function isError(e) {
  function isFunction (line 18093) | function isFunction(arg) {
  function isPrimitive (line 18098) | function isPrimitive(arg) {
  function objectToString (line 18110) | function objectToString(o) {
  function pad (line 18115) | function pad(n) {
  function timestamp (line 18124) | function timestamp() {
  function hasOwnProperty (line 18166) | function hasOwnProperty(obj, prop) {

FILE: wdio/test/pages/file-upload.js
  class FileUpload (line 6) | class FileUpload {
    method fileinput (line 7) | get fileinput() {
    method start (line 10) | get start() {
    method toggle (line 13) | get toggle() {
    method remove (line 16) | get remove() {
    method processing (line 19) | get processing() {
    method uploads (line 22) | get uploads() {
    method downloads (line 25) | get downloads() {
    method checked (line 28) | get checked() {
    method open (line 36) | async open(timeout) {
    method upload (line 46) | async upload(files, timeout) {
    method delete (line 64) | async delete(timeout) {
Condensed preview — 52 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,301K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 18,
    "preview": "github: [blueimp]\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 2139,
    "preview": "name: Test\n\non: [push, pull_request]\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-"
  },
  {
    "path": ".gitignore",
    "chars": 24,
    "preview": "*.pyc\n.env\nnode_modules\n"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1092,
    "preview": "MIT License\n\nCopyright © 2010 Sebastian Tschan, https://blueimp.net\n\nPermission is hereby granted, free of charge, to an"
  },
  {
    "path": "README.md",
    "chars": 8568,
    "preview": "# jQuery File Upload\n\n## Contents\n\n- [Description](#description)\n- [Demo](#demo)\n- [Features](#features)\n- [Security](#s"
  },
  {
    "path": "SECURITY.md",
    "chars": 8999,
    "preview": "# File Upload Security\n\n## Contents\n\n- [Introduction](#introduction)\n- [Purpose of this project](#purpose-of-this-projec"
  },
  {
    "path": "VULNERABILITIES.md",
    "chars": 5396,
    "preview": "# List of fixed vulnerabilities\n\n## Contents\n\n- [Potential vulnerabilities with PHP+ImageMagick](#potential-vulnerabilit"
  },
  {
    "path": "cors/postmessage.html",
    "chars": 2367,
    "preview": "<!DOCTYPE html>\n<!--\n/*\n * jQuery File Upload Plugin postMessage API\n * https://github.com/blueimp/jQuery-File-Upload\n *"
  },
  {
    "path": "cors/result.html",
    "chars": 580,
    "preview": "<!DOCTYPE html>\n<!--\n/*\n * jQuery Iframe Transport Plugin Redirect Page\n * https://github.com/blueimp/jQuery-File-Upload"
  },
  {
    "path": "css/jquery.fileupload-noscript.css",
    "chars": 433,
    "preview": "@charset \"UTF-8\";\n/*\n * jQuery File Upload Plugin NoScript CSS\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Co"
  },
  {
    "path": "css/jquery.fileupload-ui-noscript.css",
    "chars": 362,
    "preview": "@charset \"UTF-8\";\n/*\n * jQuery File Upload UI Plugin NoScript CSS\n * https://github.com/blueimp/jQuery-File-Upload\n *\n *"
  },
  {
    "path": "css/jquery.fileupload-ui.css",
    "chars": 1291,
    "preview": "@charset \"UTF-8\";\n/*\n * jQuery File Upload UI Plugin CSS\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyrigh"
  },
  {
    "path": "css/jquery.fileupload.css",
    "chars": 655,
    "preview": "@charset \"UTF-8\";\n/*\n * jQuery File Upload Plugin CSS\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2"
  },
  {
    "path": "docker-compose.yml",
    "chars": 1210,
    "preview": "version: '3.7'\nservices:\n  example:\n    build: server/php\n    ports:\n      - 127.0.0.1:80:80\n    volumes:\n      - .:/var"
  },
  {
    "path": "index.html",
    "chars": 14845,
    "preview": "<!DOCTYPE html>\n<!--\n/*\n * jQuery File Upload Demo\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010"
  },
  {
    "path": "js/cors/jquery.postmessage-transport.js",
    "chars": 3587,
    "preview": "/*\n * jQuery postMessage Transport Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2011, Sebasti"
  },
  {
    "path": "js/cors/jquery.xdr-transport.js",
    "chars": 3051,
    "preview": "/*\n * jQuery XDomainRequest Transport Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2011, Seba"
  },
  {
    "path": "js/demo.js",
    "chars": 2230,
    "preview": "/*\n * jQuery File Upload Demo\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010, Sebastian Tschan\n *"
  },
  {
    "path": "js/jquery.fileupload-audio.js",
    "chars": 2973,
    "preview": "/*\n * jQuery File Upload Audio Preview Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Seb"
  },
  {
    "path": "js/jquery.fileupload-image.js",
    "chars": 10781,
    "preview": "/*\n * jQuery File Upload Image Preview & Resize Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright "
  },
  {
    "path": "js/jquery.fileupload-process.js",
    "chars": 5266,
    "preview": "/*\n * jQuery File Upload Processing Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2012, Sebast"
  },
  {
    "path": "js/jquery.fileupload-ui.js",
    "chars": 24036,
    "preview": "/*\n * jQuery File Upload User Interface Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010, Se"
  },
  {
    "path": "js/jquery.fileupload-validate.js",
    "chars": 3703,
    "preview": "/*\n * jQuery File Upload Validation Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Sebast"
  },
  {
    "path": "js/jquery.fileupload-video.js",
    "chars": 2973,
    "preview": "/*\n * jQuery File Upload Video Preview Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2013, Seb"
  },
  {
    "path": "js/jquery.fileupload.js",
    "chars": 56631,
    "preview": "/*\n * jQuery File Upload Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010, Sebastian Tschan\n"
  },
  {
    "path": "js/jquery.iframe-transport.js",
    "chars": 8926,
    "preview": "/*\n * jQuery Iframe Transport Plugin\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2011, Sebastian Ts"
  },
  {
    "path": "js/vendor/jquery.ui.widget.js",
    "chars": 23520,
    "preview": "/*! jQuery UI - v1.12.1+0b7246b6eeadfa9e2696e22f3230f6452f8129dc - 2020-02-20\n * http://jqueryui.com\n * Includes: widget"
  },
  {
    "path": "package.json",
    "chars": 3138,
    "preview": "{\n  \"name\": \"blueimp-file-upload\",\n  \"version\": \"10.32.0\",\n  \"title\": \"jQuery File Upload\",\n  \"description\": \"File Uploa"
  },
  {
    "path": "server/gae-python/app.yaml",
    "chars": 290,
    "preview": "runtime: python27\napi_version: 1\nthreadsafe: true\n\nlibraries:\n  - name: PIL\n    version: latest\n\nhandlers:\n  - url: /(fa"
  },
  {
    "path": "server/gae-python/main.py",
    "chars": 7574,
    "preview": "# -*- coding: utf-8 -*-\n#\n# jQuery File Upload Plugin GAE Python Example\n# https://github.com/blueimp/jQuery-File-Upload"
  },
  {
    "path": "server/gae-python/static/robots.txt",
    "chars": 24,
    "preview": "User-agent: *\nDisallow:\n"
  },
  {
    "path": "server/php/.dockerignore",
    "chars": 11,
    "preview": "*\n!php.ini\n"
  },
  {
    "path": "server/php/Dockerfile",
    "chars": 1146,
    "preview": "FROM php:8.0.11-apache\n\n# Enable the Apache Headers module:\nRUN ln -s /etc/apache2/mods-available/headers.load \\\n  /etc/"
  },
  {
    "path": "server/php/UploadHandler.php",
    "chars": 59881,
    "preview": "<?php\n/*\n * jQuery File Upload Plugin PHP Class\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010, S"
  },
  {
    "path": "server/php/files/.gitignore",
    "chars": 25,
    "preview": "*\n!.gitignore\n!.htaccess\n"
  },
  {
    "path": "server/php/files/.htaccess",
    "chars": 2338,
    "preview": "# If you have not done so already, please first read SECURITY.md in the root\n# directory of this project or online:\n# ht"
  },
  {
    "path": "server/php/index.php",
    "chars": 347,
    "preview": "<?php\n/*\n * jQuery File Upload Plugin PHP Example\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010,"
  },
  {
    "path": "server/php/php.ini",
    "chars": 111,
    "preview": "max_execution_time = 300\nmemory_limit = 500M\npost_max_size = 4G\nupload_max_filesize = 4G\nmax_file_uploads = 50\n"
  },
  {
    "path": "test/index.html",
    "chars": 1706,
    "preview": "<!DOCTYPE html>\n<!--\n/*\n * jQuery File Upload Test\n * https://github.com/blueimp/jQuery-File-Upload\n *\n * Copyright 2010"
  },
  {
    "path": "test/unit.js",
    "chars": 28747,
    "preview": "/*\n * jQuery File Upload Test\n * https://github.com/blueimp/JavaScript-Load-Image\n *\n * Copyright 2010, Sebastian Tschan"
  },
  {
    "path": "test/vendor/chai.js",
    "chars": 345750,
    "preview": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"func"
  },
  {
    "path": "test/vendor/mocha.css",
    "chars": 5590,
    "preview": "@charset \"utf-8\";\n\nbody {\n  margin:0;\n}\n\n#mocha {\n  font: 20px/1.5 \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  mar"
  },
  {
    "path": "test/vendor/mocha.js",
    "chars": 590410,
    "preview": "(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c=\"function\"==typeof require&&require;if(!f&&c)ret"
  },
  {
    "path": "wdio/.eslintrc.js",
    "chars": 109,
    "preview": "'use strict'\n\nmodule.exports = {\n  env: {\n    node: true\n  },\n  parserOptions: {\n    ecmaVersion: 2019\n  }\n}\n"
  },
  {
    "path": "wdio/.prettierrc.js",
    "chars": 142,
    "preview": "'use strict'\n\nmodule.exports = {\n  arrowParens: 'avoid',\n  proseWrap: 'always',\n  semi: false,\n  singleQuote: true,\n  tr"
  },
  {
    "path": "wdio/LICENSE.txt",
    "chars": 1092,
    "preview": "MIT License\n\nCopyright © 2019 Sebastian Tschan, https://blueimp.net\n\nPermission is hereby granted, free of charge, to an"
  },
  {
    "path": "wdio/conf/chrome.js",
    "chars": 902,
    "preview": "'use strict'\n\n/* eslint-disable jsdoc/valid-types */\n/** @type WebdriverIO.Config */\nconst config = {\n  hostname: 'chrom"
  },
  {
    "path": "wdio/conf/firefox.js",
    "chars": 540,
    "preview": "'use strict'\n\n/* eslint-disable jsdoc/valid-types */\n/** @type WebdriverIO.Config */\nconst config = {\n  hostname: 'gecko"
  },
  {
    "path": "wdio/hooks/index.js",
    "chars": 1055,
    "preview": "'use strict'\n\n/* global browser, Promise */\n\nconst cmds = require('wdio-screen-commands')\n\n/* eslint-disable jsdoc/valid"
  },
  {
    "path": "wdio/test/pages/file-upload.js",
    "chars": 1816,
    "preview": "'use strict'\n\n/* global browser, $, $$ */\n/* eslint-disable class-methods-use-this */\n\nclass FileUpload {\n  get fileinpu"
  },
  {
    "path": "wdio/test/specs/01-file-upload.js",
    "chars": 610,
    "preview": "'use strict'\n\n/* global browser, describe, it */\n\nconst FileUpload = require('../pages/file-upload')\nconst assetsDir = b"
  },
  {
    "path": "wdio/wdio.conf.js",
    "chars": 96,
    "preview": "'use strict'\n\n// Default to the Chrome config:\nexports.config = require('./conf/chrome').config\n"
  }
]

About this extraction

This page contains the full source code of the blueimp/jQuery-File-Upload GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 52 files (1.2 MB), approximately 341.9k tokens, and a symbol index with 588 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!